這里文章都是從個人的github博客直接復制過來的,排版可能有點亂. 原始地址
http://benq.im/2015/05/12/hexomd-06/
上一篇我們實現了粘貼上傳圖片
功能.
今天將實現自動更新
的功能,有了這個功能以后我再發博客就不需要每次都把最新的程序重新打包上傳了.
對於不想看如何實現的朋友,直接下載打包好的程序就行,以后更新可以點擊軟件右上角的第一個按鈕即可(手動).

自動更新方案
在做上一個軟件Gungnir的時候,為了可以顯示更新進度,自動更新的方案是列出所有需要更新的文件,然后自動下載每個文件並覆蓋,但是在需要更新一些node模塊(文件一般都很多)時就相當麻煩了,有一個文件傳輸失敗就會導致更新出錯.實現起來相當麻煩,而且也並不能帶來什么優勢.
所以做這個軟件的自動更新的時候,我用了更為簡單粗暴的方案:將需要更新的文件打包成zip文件,直接下載並解壓覆蓋即可.
實現
自動更新作為較單獨的功能模塊,我把全部代碼放在modules/updater.js,這里就不把全部代碼貼出來了,需要的自己點鏈接看,里面有注釋. 我只講一些實現細節.
安裝依賴模塊
首先是安裝兩個新增了的node模塊依賴when
和bufferhelper
,第一個是promise
模塊,第二個看名字就知道,無須解釋.
1 |
npm install bufferhelper |
將7z.exe
放到軟件根目錄備用

增加了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 = { 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) { 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)) { 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> |
總結
基本的自動更新的功能比圖片上傳更為簡單,但是今天做的這個功能還有很多細節問題,比如:
- 無法自動刪除新版本不需要的文件
- 以后如果程序大了,更新補丁每次都全量打包會導致更新很慢
- 更新后不會自動重啟軟件.
- 更好的方案是自動根據git的提交信息生成更新列表,並且根據版本號管理.

接下來的計划:
- 雲同步.
- 插件機制
- 表情插件.
附件
本篇程序打包.
項目地址