毫無難度掌握自動更新

爲了儘快的完成模塊的編寫,我使用Node.js編寫這個模塊,桌面級處理爲Electron。

先展示一下效果,HTML就不去精益求精了。




OK,我們先想一下,要怎麼做。首先,現在的網絡空間說便宜吧,也沒到滿大街都是,更何況是一個持續可用的空間。

對於我這種學生黨,自然是不樂意去花錢買空間了。所以吧,我們借一下Github的空間好了,如何?是不是感覺很新穎?

但是Github的空間是不支持動態解析的哦!也就是說你只能下載靜態資源,OK,沒問題,動態解析可以放到客戶端嘛,需要下載哪些更新文件就下載什麼好了。

我們先創建一個Electron項目好了。

先看一下架構:


src[folder] => 自然是存放js代碼的地方咯。

    AutoUP[folder] => 自動更新模塊

UpdateStorage[folder] => 更新數據存放點

    tmp[folder] => 臨時數據區

    release.json[file] => 這個是由服務器進行維護的文件,可以在客戶端刪除,爲了方便git推送,我留着的。

    version.json[file] => 當前的版本信息

index.html[file] => Electron的主界面

LICENSE.md[file] => 許可

剩餘的就沒什麼好看的了。

好了,現在我們來想一下該怎麼實現,Github的空間理論上來說是無限的,所以不用節省,JS代碼想節省都難吧。


我們來看看怎麼將在github上的文件下載下來。


注意,我們可以直接從raw.githubusercontent.com拉取文件,當然,這個必須要有確切的文件名。

有了解過的應該都知道,電腦軟件更新,比如VS Studio,總不能全部重新下載然後再全部重新安裝吧?所以一般我們都會做一個更新索引,爲什麼呢?因爲需要什麼下載什麼就好了!

好,我們先構想一下,Node對於JSON的處理那簡直是爽炸啊!一個require就導入進來了。

所以這個索引,和傳統的索引不同,我們不使用XML格式,而是採用JSON了。

好,那麼我們該怎樣來設計這個JSON呢?

首先,最新的版本號得在裏面吧?

OK,那麼更新所需下載的文件或者更新所需要更新的文件也得存在裏面吧?

但是問題來了?我怎麼知道要下載什麼啊?我可沒有PHP,JSP動態的返回一個JSON告訴你你應該下載啥吧?

既然這樣,那倒不如直接把每次更新所需要的文件都放到JSON裏,反正一個JSON也不大,相比起其他的軟件更新,動挪30多MB的XML索引,總不會差的。

OK,那麼更新總得專業點吧?更新的說明是不是該加上?

太棒了,這麼一想,這個文件就搞定了。


我設計的就是這樣子,我們來看看佔用多少,

連1KB都不到哦!

所以這樣做肯定是沒什麼大問題的。

OK,那麼軟件更新第一步是幹什麼?

下載更新索引對吧?那麼下載總得要吧!

先拿request寫一個下載函數。

const electron = require('electron');
const request = require('request');
const fs = require('fs');
var app = null;
if (electron.app == null) {
app = electron.remote.app;
} else {
app = electron.app;
}

function downloadFile(uri, filename, callback) {
var stream = fs.createWriteStream(filename);
request(uri).pipe(stream).on('close', callback);
}

接下來就是去下載索引文件了,我是從https://raw.githubusercontent.com/1426652334/AutoUpdate/master/UpdateStorage/release.json這裏下載索引,注意自己的Github地址需要更換的。

那麼下載的代碼就是:

var fileUri = 'https://raw.githubusercontent.com/1426652334/AutoUpdate/master/UpdateStorage/release.json';
var filename = 'release.json';
var path = app.getAppPath() + '\\UpdateStorage\\tmp\\' + filename;
var release_path = app.getAppPath() + "\\UpdateStorage\\tmp\\release.json";
downloadFile(fileUri, path, function () {});

下載好了那麼就要開始分析了。

這裏我們在回調函數內分析,因爲Node的異步,如果download函數下面一行就寫分析的話,你是得不到結果的。

先把索引加載把。

因爲require加載JSON可能會報錯,所以必須使用try-catch塊包圍他們,並且我們需要將分析結果轉儲下來,這裏我用upVer數組來轉儲,同樣加載本地的版本文件,即version.json。

var release = null;
var upVer = null;
const old_v = require(app.getAppPath() + '\\UpdateStorage\\version.json');
try {
release = require(release_path);
} catch (error) {
alert("更新出錯!" + error);
}

首先要想用戶展示有哪些更新可用,並且這個更新的說明是什麼。

這個毫無技術難度。

var control = false;
for(var key in release.manifest){
    if(control){
        document.write("Version:" + key + "&nbsp;&nbsp;可用,更新說明:" + release.manifest[key]['detail'] + "<br>");
    }
    if(key == old_v.version){
        control = true;
    }
}

注意,爲什麼我可以用foreach語句來遍歷更新說明,請返回上面看一下我的release.json架構,我們必須保證,該json內的版本信息是有順序的,此時纔可以使foreach語句內的遍歷無異常。

我們在foreach內,用control來判斷foreach內的version是否超過當前的version,然後,我們在用version相同時,將control開關打開,OK,這樣就可以遍歷出來了。至於爲什麼control修改要放在後面,如果看不出來,請你仔細鞏固基礎再來看,雖然說是無難度掌握,但是基礎都不行,可能是最高難度掌握了。

好的,那麼接下來我們要開始更新文件了。

將control開關閉上,我們開始重新遍歷的歷程。

但是該怎麼遍歷呢?首先,我們要獲取對應版本內所需更新的文件,假如現在軟件版本1.0.0,但是服務器已經更新到了2.0.0,中間有版本1.0.1,和1.0.2,那麼我們需要更新的文件就是對應版本1.0.1和1.0.0以及2.0.0版本所需更新的文件了。

同樣還是和上面遍歷一樣的套路,因爲保證了release.json文件的順序性,那麼我們可以用version比較以及開關控制。

這時又會產生一個問題了,我只知道要下載的文件是什麼啊,可不知道地址是什麼啊!

別擔心,我們可以發現,所有存儲在github內的文件,都是位於"https://raw.githubusercontent.com/1426652334/AutoUpdate/master/"下的。

那麼也就是說我們的文件下載地址可以這樣構造:

https://raw.githubusercontent.com/1426652334/AutoUpdate/master/$filedir_name_included

熟悉github的人知道其實不止是這個,因爲這是拉取master branch下的文件,可能還有其他的branch,這裏不作討論了。

設置源,構造路徑,完美。

const origin = 'https://raw.githubusercontent.com/1426652334/AutoUpdate/master/';
control = false;
for(var key in release.manifest){
    if(control){
        for(var file in release.manifest[key]['fetch']){
            var fileLink = origin + release.manifest[key]['fetch'][file];
            var storage = app.getAppPath() + "\\" + release.manifest[key]['fetch'][file];
            downloadFile(fileLink, storage, function () {});
        }
        document.write('<p>更新至版本:' + key + ",請注意,此時可能尚未完成更新,請勿退出此應用。<br></p>");
    }
    if(key == old_v.version){
        control = true;
       }
}

好了,如果有更新,此時應該會下載所有更新的所需文件了。

那麼沒有更新怎麼辦呢?

if(release.version == old_v.version){
    document.write('看來今日無事可做,你的軟件已是最新版本了。')
}else{
    document.write('完成更新!請注意,軟件可能仍在下載數據,但一般來說,由於更新所需要數據十分小,此時它應該已經完成了更新。<br>您可以放心的繼續使用此軟件而不受絲毫影響。')
}

OK,沒有更新也處理完了。

但是別忘了還有最後一個結尾工作,

fs.writeFileSync(app.getAppPath() + "\\UpdateStorage\\version.json", fs.readFileSync(release_path))
fs.unlink(release_path);

OK啦,這樣子自動更新就做好啦。

是不是好簡單?

對了,跟大家說一下,我之前的想法是:

release內的manifest中的fetch沒有的,因爲我是打算直接在release.json同級目錄中存在若干個以版本號爲文件名的json文件,裏面存儲着需要的文件索引。

之所以沒有這麼實現,是因爲,這樣子異步做起來會比較麻煩,不信大家可以試試啦,還有,爲了保證客戶端的流暢運行,所以下載是異步下載的啦,如果有需要自己想辦法改成同步下載就好啦。


附該Project地址:

https://github.com/1426652334/AutoUpdate.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章