feat(remote): 实现文件下载真实进度显示

- 下载改用流式读取,计算真实进度百分比
- 通过 IPC 事件实时推送进度到前端
- 支持 Content-Length 计算下载进度
This commit is contained in:
2026-03-10 14:59:11 +08:00
parent 40f99f0c49
commit 433db24688
8 changed files with 100 additions and 21 deletions

View File

@@ -255,9 +255,9 @@ ipcMain.handle('remote-upload-file', async (_event, serverHost: string, port: nu
}
});
ipcMain.handle('remote-download-file', async (_event, serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) => {
ipcMain.handle('remote-download-file', async (_event, id: string, serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) => {
try {
log.info('Remote download params:', { serverHost, port, fileName, remotePath, localPath, password });
log.info('Remote download params:', { id, serverHost, port, fileName, remotePath, localPath, password });
const win = electronState.getMainWindow();
if (!win) {
@@ -275,8 +275,12 @@ ipcMain.handle('remote-download-file', async (_event, serverHost: string, port:
throw new Error(`Download failed: ${response.statusText}`);
}
const buffer = await response.arrayBuffer();
const contentLength = response.headers.get('Content-Length');
if (!contentLength) {
throw new Error('Server did not return Content-Length');
}
const totalSize = parseInt(contentLength, 10);
const targetDir = localPath || 'C:\\';
const targetPath = path.join(targetDir, fileName);
@@ -284,8 +288,31 @@ ipcMain.handle('remote-download-file', async (_event, serverHost: string, port:
fs.mkdirSync(targetDir, { recursive: true });
}
fs.writeFileSync(targetPath, Buffer.from(buffer));
const fileStream = fs.createWriteStream(targetPath);
const reader = response.body?.getReader();
if (!reader) {
throw new Error('Failed to get response body reader');
}
let downloadedSize = 0;
const CHUNK_SIZE = 64 * 1024;
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
if (value) {
downloadedSize += value.length;
fileStream.write(value);
const progress = Math.round((downloadedSize / totalSize) * 100);
win.webContents.send('download-progress', { id, progress });
}
}
fileStream.end();
return { success: true, filePath: targetPath };
} catch (error: any) {
log.error('Remote download failed:', error);

View File

@@ -19,6 +19,11 @@ contextBridge.exposeInMainWorld('electronAPI', {
ipcRenderer.on('remote-clipboard-auto-sync', handler);
return () => ipcRenderer.removeListener('remote-clipboard-auto-sync', handler);
},
onDownloadProgress: (callback: (data: { progress: number; id: string }) => void) => {
const handler = (_event: Electron.IpcRendererEvent, data: { progress: number; id: string }) => callback(data);
ipcRenderer.on('download-progress', handler);
return () => ipcRenderer.removeListener('download-progress', handler);
},
clipboardReadText: () => ipcRenderer.invoke('clipboard-read-text'),
clipboardWriteText: (text: string) => ipcRenderer.invoke('clipboard-write-text', text),
remoteFetchDrives: (serverHost: string, port: number, password?: string) =>
@@ -27,6 +32,6 @@ contextBridge.exposeInMainWorld('electronAPI', {
ipcRenderer.invoke('remote-fetch-files', serverHost, port, filePath, password),
remoteUploadFile: (serverHost: string, port: number, filePath: string, remotePath: string, password?: string) =>
ipcRenderer.invoke('remote-upload-file', serverHost, port, filePath, remotePath, password),
remoteDownloadFile: (serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) =>
ipcRenderer.invoke('remote-download-file', serverHost, port, fileName, remotePath, localPath, password),
remoteDownloadFile: (id: string, serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) =>
ipcRenderer.invoke('remote-download-file', id, serverHost, port, fileName, remotePath, localPath, password),
})