diff --git a/mixly-sw/mixly-modules/common/setting.js b/mixly-sw/mixly-modules/common/setting.js index 67d5f700..dbf877e0 100644 --- a/mixly-sw/mixly-modules/common/setting.js +++ b/mixly-sw/mixly-modules/common/setting.js @@ -254,6 +254,9 @@ Setting.configMenuGetValue = (obj) => { Setting.refreshUpdateMenuStatus = (localVersion, cloudVersion, needsUpdate, url) => { const $serverDiv = $('#setting-menu-update-server'); const $btnDiv = $('#setting-menu-update > div:nth-child(2)'); + const $button = $btnDiv.children('button'); + $button.removeClass('layui-btn-disabled'); + $button.addClass('self-adaption-btn'); const $mixlyProgress = $serverDiv.find('.mixly-progress'); $serverDiv.find('span').css('display', 'none'); $mixlyProgress.hide(); @@ -269,7 +272,9 @@ Setting.refreshUpdateMenuStatus = (localVersion, cloudVersion, needsUpdate, url) } if (needsUpdate) { $btnDiv.css('display', 'flex'); - $btnDiv.children('button').off().one('click', (event) => { + $button.off().one('click', (event) => { + $button.addClass('layui-btn-disabled'); + $button.removeClass('self-adaption-btn'); const eventSource = new EventSource(`/api/download?url=${encodeURIComponent(url)}&cloudVersion=${cloudVersion}`); $mixlyProgress.show(); eventSource.onmessage = function(event) { diff --git a/static-server/api.js b/static-server/api.js index a663816c..28dad8a2 100644 --- a/static-server/api.js +++ b/static-server/api.js @@ -172,72 +172,106 @@ router.get('/download', async (req, res) => { try { const { url, cloudVersion } = req.query; - if(fs.existsSync(TEMP_FOLDER_PATH)) { + // 清理临时文件夹 + if (fs.existsSync(TEMP_FOLDER_PATH)) { deleteFolderRecursive(TEMP_FOLDER_PATH); } - fs.mkdirSync(TEMP_FOLDER_PATH); + fs.mkdirSync(TEMP_FOLDER_PATH, { recursive: true }); + const filePath = path.resolve(TEMP_FOLDER_PATH, 'mixly.zip'); const fileStream = fs.createWriteStream(filePath); - // 设置响应头 + // 设置 SSE 响应头 res.setHeader('Content-Type', 'text/event-stream'); - res.setHeader('Transfer-Encoding', 'chunked'); + res.setHeader('Cache-Control', 'no-cache'); + res.setHeader('Connection', 'keep-alive'); + res.flushHeaders(); - // 发起下载请求 + // 发起下载请求 - 添加 NW.js 特定配置 const response = await axios({ method: 'GET', url: url, - responseType: 'stream' + responseType: 'stream', + timeout: 60000, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + }, + adapter: require('axios/lib/adapters/http') }); - const totalSize = parseInt(response.headers['content-length'], 10); + const totalSize = parseInt(response.headers['content-length'], 10) || 0; + let downloadedSize = 0; let lastProgress = 0; + // 发送进度信息 const sendProgress = (progress) => { - if(progress !== lastProgress) { - res.write('data:' + JSON.stringify({ type: 'progress', progress }) + '\n\n'); + if (progress !== lastProgress) { + const data = JSON.stringify({ type: 'progress', progress }); + res.write(`data: ${data}\n\n`); lastProgress = progress; } }; - // 发送解压信息 - const sendUnzip = () => { - res.write('data:' + JSON.stringify({ type: 'unzip' }) + '\n\n'); - }; - - // 发送完成信息 - const sendComplete = (version) => { - res.write('data:' + JSON.stringify({ type: 'complete', version }) + '\n\n'); - res.end(); - }; + // 管道流处理 response.data.pipe(fileStream); - // 处理数据流 + + // 进度监控 response.data.on('data', (chunk) => { downloadedSize += chunk.length; - const progress = Math.round((downloadedSize / totalSize) * 100); - sendProgress(progress); + if (totalSize > 0) { + const progress = Math.round((downloadedSize / totalSize) * 100); + sendProgress(progress); + } }); + // 文件流完成 fileStream.on('finish', async () => { - // 获取版本信息并保存 - saveVersionInfo(cloudVersion); - sendUnzip(); + console.log('文件下载完成,开始解压'); + + // 发送解压信息 + res.write(`data: ${JSON.stringify({ type: 'unzip' })}\n\n`); + try { const asyncZip = new AsyncAdmZip(filePath); await asyncZip.extractAllTo(path.resolve(__dirname, '../')); - sendComplete(cloudVersion); - if(fs.existsSync(TEMP_FOLDER_PATH)) { + + // 保存版本信息 + saveVersionInfo(cloudVersion); + + // 发送完成信息 + res.write(`data: ${JSON.stringify({ type: 'complete', version: cloudVersion })}\n\n`); + + // 清理临时文件 + if (fs.existsSync(TEMP_FOLDER_PATH)) { deleteFolderRecursive(TEMP_FOLDER_PATH); } + + res.end(); } catch (error) { - console.log(error); - res.status(500).json({ error: '解压失败' }); + console.error('解压失败:', error); + res.write(`data: ${JSON.stringify({ type: 'error', message: '解压失败' })}\n\n`); + res.end(); } }); + + // 错误处理 + response.data.on('error', (error) => { + console.error('下载流错误:', error); + res.write(`data: ${JSON.stringify({ type: 'error', message: '下载流错误' })}\n\n`); + res.end(); + }); + + fileStream.on('error', (error) => { + console.error('文件流错误:', error); + res.write(`data: ${JSON.stringify({ type: 'error', message: '文件保存错误' })}\n\n`); + res.end(); + }); + } catch (error) { - console.log(error.message); - res.status(500).json({ error: '下载失败' }); + console.error('下载过程错误:', error); + res.write(`data: ${JSON.stringify({ type: 'error', message: '下载失败: ' + error.message })}\n\n`); + res.end(); } });