问题:如何为Electron应用实现一个简易的自动更新功能
官方其实已经提供了几种很便捷的方案:https://www.electronjs.org/docs/tutorial/updates 但是不是需要github,就是需要搭建一个服务端,因为我们的场景很小,electron只是一个壳,所以更新的需求不强烈,只是一个以防万一的功能,所以我们想寻求一个简单的方式来处理。
// 堆代码 duidaima.com const { autoUpdater } = require('electron') //先设置更新的url autoUpdater.setFeedURL({url: "https://xxxxxx"}); //在合适的时机检查更新 autoUpdater.checkForUpdates();其实这样就可以了,checkForUpdates会检查更新并自动下载安装,全程无感知。当重启应用的时候就会是新版本的了。当然这是最简单的步骤,我们后面会丰富一下功能。
2023-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.UpdateManager.<CheckForUpdate>d__7.MoveNext() --- 引发异常的上一位置中堆栈跟踪的末尾 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 在 Squirrel.Update.Program.<CheckForUpdate>d__8.MoveNext() --- 内部异常堆栈跟踪的结尾 --- 在 System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) 在 System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) 在 System.Threading.Tasks.Task`1.get_Result() 在 Squirrel.Update.Program.executeCommandLine(String[] args) 在 Squirrel.Update.Program.main(String[] args) ---> (内部异常 #0) 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.UpdateManager.<CheckForUpdate>d__7.MoveNext() --- 引发异常的上一位置中堆栈跟踪的末尾 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 在 Squirrel.Update.Program.<CheckForUpdate>d__8.MoveNext()<---出现这个错误怀疑是与electron编译的安装包有关,运行安装包的时候会展示安装动画,但是安装完成已经打开应用了,动画还没有消失,有时候甚至持续几分钟。
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中还会记录相关错误,而且更新中断。所以这并不是解决办法,这样处理后会导致第一次启动更新大概率失败,不过再次启动的时候就会正常更新了,所以暂时可以接受。
2023-04-25 14:51:42> IEnableLogger: Failed to download url: https://appd.knowbox.cn/aiclass-pc-update/dev/RELEASES?id=aiclass&localVersion=0.1.0&arch=amd64: 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() 2023-04-25 14:51:42> FileDownloader: Downloading url: https://appd.knowbox.cn/aiclass-pc-update/dev/releases?id=aiclass&localversion=0.1.0&arch=amd64 2021-04-25 14:51:43> IEnableLogger: Failed to download url: https://appd.knowbox.cn/aiclass-pc-update/dev/releases?id=aiclass&localversion=0.1.0&arch=amd64: 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() 2023-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抓包发现,服务器返回的是:
{ // 堆代码 duidaima.com "code": "40310011", "msg": "invalid User-Agent header" }在charles中查看这个请求的header发现没有User-Agent,所以应该就是这里出现的问题。通过postman我们模拟请求,发现当删除User-Agent就会出现上面的错误,随便添加一个就可以正常访问。
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.setFeedURL({url: global.tmpPath});