Electron中用Ajax与FS下载保存.tar.gz文件损坏问题求助
Electron应用自动更新:下载.tar.gz文件损坏问题排查与优化
核心问题分析
二进制文件下载方式错误
你用$.get请求下载.tar.gz二进制文件,而jQuery的$.get默认以文本格式处理响应,会对二进制数据进行字符编码转换,破坏原始二进制结构,最终导致文件损坏无法解压。浏览器直接下载时会自动识别并处理二进制数据,所以不会出现问题。版本号比较逻辑错误
r["tag_name"].slice(-6) <= current_version.slice(-6)这种字符串截取对比的方式完全不可靠。比如版本v1.0.10和v1.0.2,截取后得到1.0.10和1.0.2,字符串按字符逐个对比时,会认为1.0.10小于1.0.2,直接导致新版本判断失效。
修复方案
1. 正确下载二进制文件
方案一:用Node.js的https模块(推荐在主进程执行)
Electron主进程可直接使用Node.js API,处理文件下载和IO更稳定:
// 主进程代码示例 const https = require('https'); const fs = require('fs'); const { ipcMain } = require('electron'); // 监听渲染进程的下载请求 ipcMain.on('download-update', (event, url) => { const fileStream = fs.createWriteStream('release.tar.gz'); https.get(url, (response) => { response.pipe(fileStream); fileStream.on('finish', () => { fileStream.close(); event.reply('download-complete', 'Update downloaded'); }); }).on('error', (err) => { fs.unlink('release.tar.gz', () => {}); // 删除损坏的文件 event.reply('download-error', err.message); }); });
渲染进程通过IPC通知主进程下载:
// 渲染进程代码 const { ipcRenderer } = require('electron'); $(document).on("click", "#download-new-version", () => { for (let asset of release_to_download["assets"]) { if (asset["content_type"] == "application/x-gzip") { ipcRenderer.send('download-update', asset["browser_download_url"]); } } });
方案二:在渲染进程用XMLHttpRequest处理二进制
若需在渲染进程处理,需设置响应类型为blob:
$(document).on("click", "#download-new-version", () => { for (let asset of release_to_download["assets"]) { if (asset["content_type"] == "application/x-gzip") { const xhr = new XMLHttpRequest(); xhr.open('GET', asset["browser_download_url"], true); xhr.responseType = 'blob'; xhr.onload = function() { if (this.status === 200) { // 将Blob转Buffer后写入文件(建议通过IPC通知主进程操作) const fs = require('fs').promises; this.response.arrayBuffer().then(buffer => { fs.writeFile('release.tar.gz', Buffer.from(buffer)); console.log("Update downloaded"); }); } }; xhr.onerror = function(err) { console.error('Download failed:', err); }; xhr.send(); } } });
2. 正确比较版本号
使用语义化版本比较库semver(项目中直接安装即可):
npm install semver
修改版本检查逻辑:
const semver = require('semver'); $(document).on("click", "#check-updates", () => { $.get('https://api.github.com/repos/PATH_TO_REPO/releases/latest') .then((r) => { // 去除版本号前缀(如v),提取纯版本字符串 const latestVersion = r["tag_name"].replace(/^v/, ''); const currentVersion = current_version.replace(/^v/, ''); if (semver.lte(latestVersion, currentVersion)) { $("#check-updates").html("No updates found."); } else { release_to_download = r; } }) .catch(err => console.error('Check update failed:', err)) })
若不想依赖第三方库,可自行实现版本拆分对比:
function compareVersions(v1, v2) { const arr1 = v1.replace(/^v/, '').split('.').map(Number); const arr2 = v2.replace(/^v/, '').split('.').map(Number); for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) { const num1 = arr1[i] || 0; const num2 = arr2[i] || 0; if (num1 > num2) return 1; if (num1 < num2) return -1; } return 0; } // 使用示例 if (compareVersions(latestVersion, currentVersion) <= 0) { // 无更新逻辑 }
代码优化建议
- 避免全局变量:
release_to_download作为全局变量容易引发意外覆盖,建议用闭包、模块变量或状态管理保存。 - 完善错误处理:添加GitHub API请求失败、下载中断的
catch逻辑,避免静默失败。 - 文件操作移至主进程:Electron渲染进程操作文件系统存在安全风险,主进程的Node.js API更稳定,建议将下载、写入逻辑放在主进程,通过IPC与渲染进程通信。
- 添加文件校验:下载完成后对比GitHub返回的asset的
sha256哈希值,确保文件未损坏:// 主进程校验示例 const crypto = require('crypto'); const fs = require('fs'); const hash = crypto.createHash('sha256'); const stream = fs.createReadStream('release.tar.gz'); stream.on('data', (data) => hash.update(data)); stream.on('end', () => { const fileHash = hash.digest('hex'); // 对比GitHub返回的asset.sha256 if (fileHash === asset.sha256) { console.log('文件校验通过'); } else { console.error('文件损坏,校验失败'); fs.unlink('release.tar.gz', () => {}); } }); - 增强UI反馈:添加下载进度条、错误提示,提升用户体验。
内容的提问来源于stack exchange,提问作者Radvvan
相关产品推荐
相关产品推荐

