目次
序文
前回の記事「【Electronで楽しむ】超詳細チュートリアル」で更新の問題について触れましたが、内容が長くなったため、別ブログとして公開することになり、この記事が誕生しました。
Electron のアップデートに関しては、公式が実際にいくつかの非常に便利なソリューションを提供しています。
ただし、github が必要か、サーバーを構築する必要があります。シーンが小さく、electron は単なるシェルであるため、更新の必要性は低く、万が一に備えた機能であるため、簡単な対処方法。
アップデート機能搭載
パッケージングにはelectron-forgeを使用していますが、実はこれにはautoUpdaterというアップデート機能が付いています。使い方も非常に簡単で、次のようないくつかの手順を実行するだけです。
const {
autoUpdater } = require('electron')
//先设置更新的url
autoUpdater.setFeedURL({
url: "https://xxxxxx"});
//在合适的时机检查更新
autoUpdater.checkForUpdates();
実際、これで十分です。checkForUpdates は、プロセス全体を通じて何も認識することなく、アップデートをチェックし、自動的にダウンロードしてインストールします。アプリケーションを再起動すると、新しいバージョンになります。
もちろん、これは最も簡単なステップであり、後で機能を充実させます。
ここにはいくつかの問題があります。
まず、Mac でアップデートしたい場合は、署名されたアプリケーションである必要がありますが、現在、Mac アプリケーションは署名されていないため、使用できず、プロンプトが表示されます。
Error: Could not get code signature for running application
2つ目はURLの更新ですが、このアドレスは何に相当するのでしょうか?更新サービスを簡単かつ迅速に構築するにはどうすればよいでしょうか?
公式ドキュメントには、このアドレスが何に対応するのかについての詳細な説明はありません。複数の公式サービスのバックグラウンドを使用している場合は、バックグラウンド インターフェイスから直接更新を追加でき、他のものを気にする必要がないからです。ただし、公式のソリューションを使用する予定はないので、この URL が何に対応するかを調べる必要があります。ファイルですか?設定データ?
URLの後ろに何か
数日間模索した後、関連ドキュメントとソース コードを参照し、最終的に URL の背後にあるものを特定しました。現時点ではウィンドウのみを考慮しているため、以下はウィンドウに基づいています。
forge を使用して squirrel-maker を通じて Windows インストール パッケージを作成します。作成後のファイル パスはプロジェクトのルート ディレクトリ /out/make/squirrel.windows/x64/xxxx.exe になります。
RELEASES
ただし、他の 2 つのファイルとファイルも同じディレクトリに生成されますxxx.nupkg
。これは更新に必要なファイルであり、RELEASES
nupkg ファイルの完全な名前、SHA512 (検証用)、およびファイル サイズを記録する構成ファイルと同等です。 、次のように:
674802FE0AE3B272F5182E4626893FDB2D8D2107 xxxxxx-0.1.0-full.nupkg 76560314
そこで、これら 2 つのファイルをファイル サーバーにアップロードし、同じディレクトリ (または仮想ディレクトリ) に配置し、ディレクトリのアドレスを feedUrl に設定します。このようにして、autoUpdater はこのディレクトリに RELEEASES ファイルを自動的にダウンロードして構成を読み取り、取得したファイル名を使用して更新ファイルをダウンロードして検証し、成功するとバックグラウンドで自動的にインストールします。
アプリケーションのルート ディレクトリを観察すると、実際にはアプリケーションのルート ディレクトリに異なるバージョン番号の名前が付けられたディレクトリがあることがわかります。バックグラウンド インストールでは、実際には新しいバージョンをダウンロードし、新しいバージョンのディレクトリに解凍します。ルート ディレクトリにバージョン番号を追加して再起動すると、実行ファイル exe は新しいバージョン番号を持つディレクトリ内のファイルを使用して実行され、更新が完了します。ファイルの古いバージョンは実際にはまだルート ディレクトリに存在します。ファイルを削除したり変更する必要がないため (変更する必要がある構成ファイルはほとんどありません)、認識されることなくインストールされるのはそのためです。
質問
実際にはそれほどスムーズにはいきませんでしたが、途中で発生したいくつかの問題点を以下にまとめます。
エラーポップアップ文字化け 詳細ログを確認してください
Electron の実行中にエラーが発生した場合は、ポップアップ ウィンドウが表示されますが、実際の操作では、エラー メッセージに中国語が含まれていると、エラー メッセージが文字化けしてしまいます。これでは正確な情報を見ることができなくなります。
どうやって対処すればいいのでしょうか?
アプリケーションのルート ディレクトリ (インストール ディレクトリ、通常は c:/user/[ユーザー名]/AppData/Local/[アプリケーション名]) に、SquirrelSetup.log というログ ファイルが生成され、アプリケーションの詳細情報が記録されます。エラー。
ロックを取得できませんでした。別のインスタンスが実行されていますか
次のように、SquirrelSetup.log をチェックして、このエラーの詳細を確認してください。
2021-04-25 15:09:13> SingleGlobalInstance: Failed to grab lockfile, will retry: C:\Users\guozh\AppData\Local\Temp\.squirrel-lock-68CEC12091756AFBF3BF0445D48359FFDABDAB12: System.IO.IOException: 文件“C:\Users\guozh\AppData\Local\Temp\.squirrel-lock-68CEC12091756AFBF3BF0445D48359FFDABDAB12”正由另一进程使用,因此该进程无法访问此文件。
在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
在 System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
在 System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
在 Squirrel.SingleGlobalInstance..ctor(String key, TimeSpan timeOut)
2021-04-25 15:09:14> Unhandled exception: System.AggregateException: 发生一个或多个错误。 ---> System.Exception: Couldn't acquire lock, is another instance running
在 Squirrel.SingleGlobalInstance..ctor(String key, TimeSpan timeOut)
在 Squirrel.UpdateManager.<acquireUpdateLock>b__32_0()
在 System.Threading.Tasks.Task`1.InnerInvoke()
在 System.Threading.Tasks.Task.Execute()
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
...
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
在 Squirrel.Update.Program.<CheckForUpdate>d__8.MoveNext()<---
このエラーは、electron によってコンパイルされたインストール パッケージに関連していると考えられます。インストール パッケージを実行すると、インストール アニメーションが表示されますが、インストール完了後にアプリケーションが開かれています。アニメーションは消えず、場合によっては表示されなくなります。数分間続きます。
アプリケーションは開くとすぐに更新されるため、この時点でアプリケーションのプロセスとインストーラーのプロセスの間に競合が発生し、上記の問題が発生する可能性があります。
現時点では、この問題は十分に回避されていませんが、次のように autoUpdater のエラー イベントを登録して処理することで回避できます。
autoUpdater.on('error', (error) => {
//dialog.showMessageBox({message:"error:" + error.name + "," + error.message + "," + error.stack})
console.log("error:" + error.name + "," + error.message + "," + error.stack)
});
このようなコードを追加すると、ポップアップ プロンプトは表示されなくなります。しかし、実際の問題は依然として存在しており、関連するエラーが SquirrelSetup.log に記録され、更新は中断されます。
これで解決というわけではなく、この処置を行うと初回起動時のアップデートは高確率で失敗しますが、再起動すると正常にアップデートされるので、とりあえずは許容範囲です。
サーバー403
次のように、SquirrelSetup.log をチェックして、このエラーの詳細を確認してください。
2021-04-25 14:51:43> CheckForUpdateImpl: Download resulted in WebException (returning blank release list): System.Net.WebException: 远程服务器返回错误: (403) 已禁止。
在 System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
在 System.Net.WebClient.GetWebResponse(WebRequest request, IAsyncResult result)
在 System.Net.WebClient.DownloadBitsResponseCallback(IAsyncResult result)
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
在 Squirrel.Utility.<LogIfThrows>d__43`1.MoveNext()
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
在 Squirrel.FileDownloader.<DownloadUrl>d__3.MoveNext()
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
在 Squirrel.UpdateManager.CheckForUpdateImpl.<CheckForUpdate>d__2.MoveNext()
実際、上記はサーバーが 403 を返したことを示しているだけで、その理由は説明されていません。URL は問題なく、ファイルも存在し、ブラウザでアクセスできるのに、なぜ 403 が表示されるのでしょうか。最終的に、Charles を通じて、サーバーが次のメッセージを返したことが判明しました。
{
"code": "40310011",
"msg": "invalid User-Agent header"
}
charles でこのリクエストのヘッダーを確認すると、User-Agent がないことがわかります。したがって、ここで問題が発生するはずです。
postman でリクエストをシミュレートしたところ、User-Agent を削除すると上記のエラーが発生するが、User-Agent を追加するだけで正常にアクセスできることがわかりました。
アプリケーションは、electron に付属するアップデートを使用するため、このリクエストに介入できないため、サーバーから開始します。テストの結果、Qiniu にはそのような問題はなく、User-Agent がなくても正常にアクセスできることが判明したため、upyun の何らかの設定が必要になるはずです。
Qiniu の置き換えが成功すると、通常どおりにアクセスできるようになります。
フル機能
上記は最も簡単な手順ですが、アプリを開くと自動的にアップデートを検出し、アップデートされている場合は自動的にダウンロードしてインストールされます。ユーザーには意識がないため、いつ更新すればよいのかがわかりません。また、新しいバージョンは、ユーザーがアプリケーションを閉じて再起動した後にのみ使用されます。したがって、ユーザーに通知する必要があります。
autoUpdater には多くのイベント コールバックがあります。上でエラーについて説明しました。これらのイベントをリッスンすることでユーザーに通知し、更新機能を実現します。公式ソリューションと比較して、よりシンプルで軽量です。更新する必要があるのは 2 つだけです将来的にはサーバー上のファイル。
完全なソースコードは次のとおりです。
const {
app, BrowserWindow, globalShortcut, Menu, autoUpdater, dialog } = require('electron')
var win;
if(require('electron-squirrel-startup')){
return app.quit();
}
function createWindow () {
win = new BrowserWindow({
width: 1280,
height: 744,
minWidth: 1280,
minHeight: 744,
webPreferences: {
nodeIntegration: true,
webSecurity: false
}
})
win.loadURL('https://appd.knowbox.cn/class/#main')
globalShortcut.register('Alt+CommandOrControl+Shift+D', () => {
win.webContents.openDevTools({
mode:'detach'}) //开启开发者工具
})
//设置url
autoUpdater.setFeedURL({
url: "https://xxxxxx"});
//开始检查更新回调
autoUpdater.on('checking-for-update', () => {
//dialog.showMessageBox({message:"check for update"})
console.log("check for update")
});
//有新版本回调
autoUpdater.on('update-available', () => {
//dialog.showMessageBox({message:"update-available"})
console.log("update-available")
});
//已下载完成回调,这里弹窗提示用户
autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName, releaseDate, updateURL) => {
dialog.showMessageBox({
message:"新版本已准备好,是否现在重启程序?", title: "更新应用", buttons: ['重启程序', '暂不重启']}).then((value) => {
if(value.response == 0){
//重启并安装新版本
autoUpdater.quitAndInstall()
}
})
console.log("update-downloaded:" + updateURL)
});
//没有新版本回调
autoUpdater.on('update-not-available', () => {
//dialog.showMessageBox({message:"update-not-available"})
console.log("update-not-available")
});
//更新出错回调
autoUpdater.on('error', (error) => {
//dialog.showMessageBox({message:"error:" + error.name + "," + error.message + "," + error.stack})
console.log("error:" + error.name + "," + error.message + "," + error.stack)
});
//检查更新
autoUpdater.checkForUpdates();
//或者可以设置成定时循环检查,比如10分钟
// setInterval(() => {
// autoUpdater.checkForUpdates();
// }, 60000);
}
Menu.setApplicationMenu(null)
app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required');
app.whenReady().then(createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
ローカルアップデート
公式は、更新パッケージを手動でローカルにダウンロードしてからローカルで更新するという解決策も提供していますが、上記ほど単純ではありませんが、一緒に調査したため、それも簡単に記録されます。
この部分のダウンロードについては説明しません。インターネット上のドキュメントを参照するだけです。主にローカルファイルの場所とアップデートについて話します。
Electron は一時ファイルをどのように保存しますか?また、それらを保存する最適な場所はどこですか? 公式ウェブサイトには良い例が記載されており、コードは次のとおりです。
var path = require('path');
var fs = require('fs');
global.tmpPath = path.join( app.getPath("temp"), "AICLASS");
if( !fs.existsSync(global.tmpPath)){
fs.mkdirSync(global.tmpPath);
}
このようにして、一時ディレクトリ tmpPath を取得しましたが、このディレクトリはどこにあるのでしょうか?
その場所はc:/用户/[用户名]/AppData/Local/Temp/AICLASS
実際にはブラウザのキャッシュ ディレクトリであり、AICLASS は独自に定義したディレクトリです。
ファイルをこのディレクトリにダウンロードし、autoUpdater を使用してローカルで更新できます。これは、次のように feedUrl がローカル ディレクトリになる点を除いてネットワーク更新と同じです。
autoUpdater.setFeedURL({url: global.tmpPath});
要約する
要約すると、Electron のアップデートは実際には非常に簡単で、アップデート中にアプリケーションを閉じる必要はなく、アプリケーションは引き続き通常どおり使用でき、再インストール後は新しいバージョンを直接使用できることがわかります。アップデート後のオープニング。
インストールディレクトリを観察するとわかります。 Electron のインストールディレクトリの下にバージョンに応じたディレクトリがあり、その下に本体ファイルが置かれています。 アップデート時には新しいバージョンのディレクトリが作成され、そのファイルが保存されます。ここでダウンロードしたファイルは変更されず、アプリケーションの動作には影響しません。更新後にアプリケーションを再度開くと、アプリケーションは自動的に新しいバージョンを見つけて新しいバージョンのファイルを実行するため、更新は成功します。したがって、Electron はサイレント アップデートを実現できます。