Electron を使用して Windows 用のアプリを作成しています。パッケージ化して配布するには、electron-builderを使用しています。Electron-builder は多くのパッケージに依存しており、自動更新にはSquirrel-windowsを使用します。
私は Windows で 3 日間自動更新と戦ってきましたが、最終的には問題がないように見える実用的なソリューションを思いつきました。
私が試して失敗したことの詳細については触れません。代わりに、私が思いついた解決策をここに投稿します。
私のシステムが故障する原因となる欠陥を指摘してくれるかどうかを確認するため、またはそれが本当に堅実な解決策である場合は、私のように苦労している人々を助けるために、皆さんと共有しています. この後者の理由から、必要以上のコードを投稿して、他の人の助けになることを願っています。
ロジックは次のとおりです。
- 現在の実行可能ファイルのパス内にサブフォルダー
fullupdate
が存在しない場合 (後で説明します)、オンライン サーバーに接続し、現在のアプリ バージョンを送信して更新があるかどうかを確認します。 - 更新がない場合は、何もしません。
- 更新がある場合は、によって生成されたインストーラー
json
をダウンロードできる URL を含む文字列を返すようにサーバーに指示します。注意: (サーバーコードは提供されていません) ではありません。.exe
electron-builder
.nupkg
:-)
- ファイルをダウンロードし、
fullupdate
アプリが現在保存されているローカル フォルダーのサブ フォルダー内に保存します。electron-builder
アプリを現在のユーザー フォルダーに保存するため、これは "安全" である必要があるAppData
ため、アクセス許可の問題は発生しません。 - ダウンロードの最後に、ダウンロードが正常に終了したことを確認するために
update
、フォルダー内に新しいファイルを作成します。fullupdate
ファイルの名前を変更することもできますが、私はこの方法を好みます。 - 次回アプリを開いたとき:
- フォルダー
fullupdate
が存在する場合は、ファイルupdate
が存在するかどうかを確認します。存在しない場合は、ダウンロードが完了していないため、フォルダーを削除しfullupdate
、リモート サーバーを呼び出して最初からやり直します。 - ファイル
update
が存在する場合は、.exe
ダウンロードしたファイルを起動し、true を返します。これにより、アプリがメイン ウィンドウを開くことができなくなります。すばらしいことに、アップデーターは に保存されている古いバージョンのアプリ全体を削除しAppData
(ローカル ユーザー データは残します)、新しいバージョンに置き換えます。このようにして、フォルダも取り除きますfullupdate
。
- フォルダー
今コード:
// we want to run this only on windows
var handleStartupEvent = function() {
if (process.platform !== 'win32') {
return false;
}
/////////////////
// MANUAL UPDATER
/////////////////
var appFolder = 'app-' + appVersion;
var pathApp = path.dirname(process.execPath);
var pathUpdate = pathApp + '\\fullupdate';
var checkupdateurl = 'https://api.mysite.com/getjson/' + appVersion.split('.').join('-');
function checkIfDownloaded(){
if (!fs.existsSync(pathUpdate)) checkUpdate();
else return checkIfInstallLocal();
}
function checkIfInstallLocal(){
if(fileExists('fullupdate\\update')) return installLocal();
else {
deleteFolderRecursive(pathUpdate);
checkUpdate();
}
}
function installLocal(){
cp.exec('fullupdate\\Update.exe', function( error, stdout, stderr){
if ( error != null ) {
console.log(stderr);
}
});
return true;
}
// from http://www.geedew.com/remove-a-directory-that-is-not-empty-in-nodejs/
var deleteFolderRecursive = function(path) {
if( fs.existsSync(path) ) {
fs.readdirSync(path).forEach(function(file,index){
var curPath = path + "/" + file;
if(fs.lstatSync(curPath).isDirectory()) deleteFolderRecursive(curPath);
else fs.unlinkSync(curPath);
});
fs.rmdirSync(path);
}
};
// from http://stackoverflow.com/questions/4482686/check-synchronously-if-file-directory-exists-in-node-js
function fileExists(path) {
try {
return fs.statSync(path).isFile();
}
catch (e) {
if (e.code == 'ENOENT') { // no such file or directory. File really does not exist
return false;
}
throw e; // something else went wrong, we don't have rights, ...
}
}
function checkUpdate(){
https.get('https://api.mysite.com/getjson/' + app.getVersion().split('.').join('-'), (res) => {
res.setEncoding('utf8');
res.on('data', function(chunk) {
if(chunk) thereIsUpdate(chunk);
});
}).on('error', (e) => {
console.log(e);
});
}
function thereIsUpdate(chunk){
var data = JSON.parse(chunk);
if(data && data.url) getNewUpdate(data.urlsetup);
}
function getNewUpdate(url){
fs.mkdirSync(pathUpdate);
var file = fs.createWriteStream(pathUpdate + '/Update.exe');
var responseSent = false; // flag to make sure that response is sent only once.
var request = https.get(url, function(response) {
response.pipe(file);
file.on('finish', () =>{
file.close(() => {
if(responseSent) return;
responseSent = true;
});
fs.closeSync(fs.openSync(pathUpdate + '/update', 'w'));
});
});
}
if(checkIfDownloaded()) return true;
/////////////////////////
// SQUIRREL EVENTS HANDLER
//////////////////////////
// see http://stackoverflow.com/questions/30105150/handle-squirrels-event-on-an-electron-app
};
// here we call the function. It is before the opening of the window, so that we prevent the opening if we are updating, or if there is a Squirrel event going on (see SO question, link above)
if (handleStartupEvent()) {
return;
}