这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/05/12/hexomd-06/
 

上一篇我们实现了粘贴上传图片功能.

今天将实现自动更新的功能,有了这个功能以后我再发博客就不需要每次都把最新的程序重新打包上传了.

对于不想看如何实现的朋友,直接下载打包好的程序就行,以后更新可以点击软件右上角的第一个按钮即可(手动).

自动更新方案

在做上一个软件Gungnir的时候,为了可以显示更新进度,自动更新的方案是列出所有需要更新的文件,然后自动下载每个文件并覆盖,但是在需要更新一些node模块(文件一般都很多)时就相当麻烦了,有一个文件传输失败就会导致更新出错.实现起来相当麻烦,而且也并不能带来什么优势.

所以做这个软件的自动更新的时候,我用了更为简单粗暴的方案:将需要更新的文件打包成zip文件,直接下载并解压覆盖即可.

实现

自动更新作为较单独的功能模块,我把全部代码放在modules/updater.js,这里就不把全部代码贴出来了,需要的自己点链接看,里面有注释. 我只讲一些实现细节.

安装依赖模块

首先是安装两个新增了的node模块依赖whenbufferhelper,第一个是promise模块,第二个看名字就知道,无须解释.

1
npm install when --save
1
npm install bufferhelper --save

7z.exe放到软件根目录备用

自定义package.json

增加了updater配置节点,配置最新的版本号version和对应的补丁文件地址package,由于我这个软件功能很少,代码并不多,因此我现在每次更新都是包含之前所有补丁的文件打包,加起来也才1m多,这样实现比较简单,只要下载最新的包即可.

1
2
3
4
5
...
"updater":{
"version":"0.6.0.1",
"package":"http://7xit5q.com1.z0.glb.clouddn.com/update.zip"
}

updater.js

updater.js里实现了hmd.updater模块,包含4个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//配置文件,用于判断是否有新版本
var packageFile = 'https://raw.githubusercontent.com/benqy/hexomd/master/package.json',
//当前程序的运行目录
execPath = require('path').dirname(process.execPath),
//补丁文件存放目录
updatePath = execPath + '\\update',
fs = require('fs'),
util = require('./helpers/util'),
when = require('./node_modules/when');
var checkUpdateTimer;
hmd.updater = {
//下载指定url的内容并返回promise对象
get: function (url) {
...
},
//检查更新
checkUpdate: function () {
...
},
//下载补丁包
update:function(packageUrl){
...
},
//安装补丁包
install: function () {
...
}
};

更新的流程为 : checkUpdate检查是否有更新 > update下载补丁包 > install安装补丁包

get方法里要注意的是下载下来的内容要判断是否经过GZIP压缩,如果是,则要用node自带的zlib解压.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
req = protocolModule.get(urlOpt, function (res) {
//是否经过gzip压缩
var isGzip = !!res.headers['content-encoding'] && !!~res.headers['content-encoding'].indexOf('gzip');
...
if (isGzip) {
require('zlib').unzip(buffer, function (err, buffer) {
gzipDeferred.resolve(buffer);
});
}
else {
gzipDeferred.resolve(buffer);
}
...
return deferred.promise;

checkUpdate方法里先下载线上的package.json文件与本地进行比较,如果版本号不一致,则提示用户更新.如果用户选择更新,则下载package.json到本地,然后调用update方法下载补丁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
checkUpdate: function () {
hmd.msg('===正在检查更新===');
//超时检查
checkUpdateTimer =setTimeout(function(){
hmd.msg('===更新失败,可能github被墙了===', hmd.MSG_LEVEL.error);
}, 10000);
var locPackage = require('nw.gui').App.manifest;
//获取版本信息和更新文件列表
hmd.updater.get(packageFile)
.then(function (packageData) {
clearTimeout(checkUpdateTimer);
packageData.text = packageData.buffer.toString();
if (!packageData.text) return;
var remotePackage = JSON.parse(packageData.text);
if (remotePackage.updater.version != locPackage.updater.version){
if (confirm('是否更新到最新版本:' + remotePackage.updater.version)) {
//如果update目录不存在则创建
if (!fs.existsSync(updatePath)) {
util.mkdir(updatePath, true);
}
//保存最新的配置文件
fs.writeFileSync(updatePath + '\\package.json', packageData.buffer);
//下载补丁包
hmd.updater.update(remotePackage.updater.package);
}
}
else {
hmd.msg('当前版本:' + remotePackage.updater.version + ',已经是最新版');
}
});
}

update方法下载补丁包到update目录,然后调用install安装补丁

1
2
3
4
5
6
7
8
update:function(packageUrl){
hmd.msg('===正在下载更新文件===', hmd.MSG_LEVEL.warnings);
hmd.updater.get(packageUrl + '?' + new Date() * 1)
.then(function (data) {
fs.writeFileSync(updatePath + '\\update.zip',data.buffer);
hmd.updater.install();
});
},

install将补丁包通过7z.exe解压覆盖到程序目录,然后提示用户重启软件.

1
2
3
4
5
6
7
8
9
install: function () {
//移动配置文件
require("child_process").exec('xcopy "' + updatePath + '\\package.json" "' + execPath + '\\package.json" /s /e /y');
//解压缩补丁文件
var unzip = execPath + '\\7z.exe x '+ updatePath +'\\update.zip -y';
require("child_process").exec(unzip,function(){
hmd.msg('===更新完成,重启后生效===');
});
}

这里我直接用7z.exe,反正也不大,也可以使用一些开源的node压缩模块.

绑定更新按钮

更新模块完成了,现在将功能绑定到按钮上.
先在modules/directives增加新的directivehmdUpdate

1
2
3
4
5
6
7
8
angular.module('hmd.directives', [])
.directive('hmdUpdate', [function () {
return function (scope, elem) {
$(elem[0]).on('click', function () {
hmd.updater.checkUpdate();
});
};
}])

然后将index.html上的更新按钮与directive绑定

1
<a class="btn rectbtn" href="javascript://" title="点击检查更新" hmd-update>...</a>

别忘了引用updater.js

1
2
3
<script src="modules/app.js"></script>
<script src="modules/updater.js"></script>
<script src="modules/directives.js"></script>

总结

基本的自动更新的功能比图片上传更为简单,但是今天做的这个功能还有很多细节问题,比如:

  1. 无法自动删除新版本不需要的文件
  2. 以后如果程序大了,更新补丁每次都全量打包会导致更新很慢
  3. 更新后不会自动重启软件.
  4. 更好的方案是自动根据git的提交信息生成更新列表,并且根据版本号管理.

接下来的计划:

  1. 云同步.
  2. 插件机制
  3. 表情插件.

附件

本篇程序打包.
项目地址

自己动手开发更好用的markdown编辑器-06(自动更新)的更多相关文章

  1. 自己动手开发更好用的markdown编辑器-04(实时预览)

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/04/25/hexomd-04/   程序打包   文章目录 1. 打开新窗口 ...

  2. 自己动手开发更好用的markdown编辑器-07(扩展语法)

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/05/19/hexomd-07/   文章目录 1. 准备工作 2. 目录语法 ...

  3. 自己动手开发更好用的markdown编辑器-05(粘贴上传图片)

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/04/28/hexomd-05/   文章目录 1. 七牛云存储 1.1. 系统 ...

  4. 自己动手制作更好用的markdown编辑器-01

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址  http://benq.im   文章目录 1. 简介 2. 项目结构 3. 程序主界面 4. 拖动窗口 5. app ...

  5. 自己动手制作更好用的markdown编辑器-03

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/04/24/hexomd-03/ 文章目录 1. 系统模块 2. 记录上次打开的 ...

  6. 自己动手制作更好用的markdown编辑器-02

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im 文章目录 1. 工具条 1.1. 样式 1.2. 工具条截图 2. 状态栏消息 3. 文件 ...

  7. 任由文字肆意流淌,更自由的开源 Markdown 编辑器

    对于创作平台来说内容编辑器是十分重要的功能,强大的编辑器可以让创作者专注于创作"笔"下生花.而最好取悦程序员创作者的方法之一就是支持 Markdown 写作,因为大多数程序员都是用 ...

  8. 用nw.js开发markdown编辑器-已完成功能介绍

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/10/29/hexomd-introduction   文章目录 1. 功能列表 ...

  9. Markdown编辑器开发记录(二):Markdown编辑器的使用与开发入门

    Markdown编辑器的使用与开发入门 在部门做技术分享的时候简单整理了一下手里的资料 1 是什么 1.1 Markdown是一种轻量级标记语言 Markdown是一种轻量级标记语言,创始人为约翰·格 ...

随机推荐

  1. Centos6.5安装mysql5.7详解

    最近在linux上面安装mysql5.7上真是遇到了很多坑,真是让人头疼,在这里跟大家简单分享一下流程跟注意的地方. 1.查看linux版本是6.5 cat /etc/redhat-release 2 ...

  2. Codeforces 863F - Almost Permutation

    863F - Almost Permutation 题意 给出每个位置可以放的数字的范围,定义 \(cost = \sum_{i=1}^{n}(cnt(i))^2\) ,其中 \(cnt(i)\) 为 ...

  3. 并查集【p2700】逐个击破

    题目描述-->p2700 逐个击破 题意概括 花费最小的代价,使得一些有标记的节点不连通. 分析 我们需要花费最小代价使得原来连通的图中一些节点之间不相互连通. 贪心显然是可行的(一点也不显然 ...

  4. 一条命令搞定在VMware中的Ubuntu14.04 64 位安装Docker

    对,就是这么炫酷! curl -sSL https://get.docker.com/ | sudo sh 如果提示没有装curl就apt-get install一下,另外提醒一下必须是64位的ubu ...

  5. 【二分答案】【Heap-Dijkstra】bzoj2709 [Violet 1]迷宫花园

    显然最短路长度随着v的变化是单调的,于是可以二分答案,据说spfa在网格图上表现较差. #include<cstdio> #include<cstring> #include& ...

  6. STL之priority_queue2

    描述 使用STL中的优先队列,将一个字符串中的各个字符按照ASCII从小到大顺序排列. 部分代码已经给出,请补充完整,提交时请勿包含已经给出的代码. int main() { int n; cin&g ...

  7. 微服务之SpringCloud实战(五):SpringCloud Eureka详解

    Eureka详解 在第三节高可用中,实际已经讲解了服务的注册,只不过注册的是Eureka本身,原理相同,通过这几篇文章我相信大家对Eureka有了一定的了解,三个核心角色:服务注册中心.服务提供者和服 ...

  8. 使用DFS求任意两点的所有路径

    先上代码: public static void findAllPaths(Integer nodeId,Integer targetNodeId, Map<Integer,ArrayList& ...

  9. 使用Maven下载jar包

    有些开源项目不直接提供jar包的下载,而是建议使用Maven下载,以开源库hipster(https://github.com/citiususc/hipster,http://www.hipster ...

  10. 将html文档转成pdf

    (1)使用场景:在项目中使用到了合同,只有在合同的头部,是不相同的.在合同的主体部分都是相同的,因此就把他放到了模板(html文件)里面. 在用户线上签约完成之后,可以将pdf版的合同下载. (2)需 ...