feat(remote): 文件传输改用Electron IPC通道

- 主进程新增4个IPC handler处理远程文件操作
- 前端通过IPC调用而非浏览器fetch访问远程API
- Remote服务新增3003端口专门处理文件传输
- 上传使用文件路径方案,下载使用保存对话框方案
This commit is contained in:
2026-03-10 00:34:02 +08:00
parent 788757b785
commit 6d5520dfa5
7 changed files with 221 additions and 98 deletions

View File

@@ -156,6 +156,142 @@ ipcMain.handle('clipboard-write-text', async (event, text: string) => {
}
});
ipcMain.handle('remote-fetch-drives', async (_event, serverHost: string, port: number, password?: string) => {
try {
let url = `http://${serverHost}:${port}/api/files/drives`;
if (password) {
url += `?password=${encodeURIComponent(password)}`;
}
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch drives: ${response.statusText}`);
}
const data = await response.json();
const items = data.items || [];
return {
success: true,
data: items.map((item: { name: string; isDirectory: boolean; size: number }) => ({
name: item.name,
path: item.name,
type: item.isDirectory ? 'dir' : 'file',
size: item.size,
modified: '',
}))
};
} catch (error: any) {
log.error('Remote fetch drives failed:', error);
return { success: false, error: error.message };
}
});
ipcMain.handle('remote-fetch-files', async (_event, serverHost: string, port: number, filePath: string, password?: string) => {
try {
let url = `http://${serverHost}:${port}/api/files/browse?path=${encodeURIComponent(filePath)}&allowSystem=true`;
if (password) {
url += `&password=${encodeURIComponent(password)}`;
}
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch files: ${response.statusText}`);
}
const data = await response.json();
const items = data.items || [];
return {
success: true,
data: items.map((item: { name: string; isDirectory: boolean; size: number; modified: Date }) => ({
name: item.name,
path: data.currentPath ? `${data.currentPath}/${item.name}` : item.name,
type: item.isDirectory ? 'dir' : 'file',
size: item.size,
modified: item.modified?.toString(),
}))
};
} catch (error: any) {
log.error('Remote fetch files failed:', error);
return { success: false, error: error.message };
}
});
ipcMain.handle('remote-upload-file', async (_event, serverHost: string, port: number, filePath: string, remotePath: string, password?: string) => {
try {
const win = electronState.getMainWindow();
if (!win) {
throw new Error('No window found');
}
const fullPath = path.resolve(filePath);
if (!fs.existsSync(fullPath)) {
throw new Error('File not found');
}
const fileBuffer = fs.readFileSync(fullPath);
const fileName = path.basename(fullPath);
let url = `http://${serverHost}:${port}/api/files/upload`;
if (password) {
url += `?password=${encodeURIComponent(password)}`;
}
const formData = new FormData();
const blob = new Blob([fileBuffer]);
formData.append('file', blob, fileName);
if (remotePath) {
formData.append('path', remotePath);
}
const response = await fetch(url, {
method: 'POST',
body: formData,
});
if (!response.ok) {
throw new Error(`Upload failed: ${response.statusText}`);
}
return { success: true };
} catch (error: any) {
log.error('Remote upload failed:', error);
return { success: false, error: error.message };
}
});
ipcMain.handle('remote-download-file', async (_event, serverHost: string, port: number, fileName: string, remotePath: string, password?: string) => {
try {
const win = electronState.getMainWindow();
if (!win) {
throw new Error('No window found');
}
let url = `http://${serverHost}:${port}/api/files/${encodeURIComponent(fileName)}`;
if (password) {
url += `?password=${encodeURIComponent(password)}`;
}
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Download failed: ${response.statusText}`);
}
const buffer = await response.arrayBuffer();
const { filePath } = await dialog.showSaveDialog(win, {
title: '保存文件',
defaultPath: fileName,
});
if (!filePath) {
return { success: false, canceled: true };
}
fs.writeFileSync(filePath, Buffer.from(buffer));
return { success: true, filePath };
} catch (error: any) {
log.error('Remote download failed:', error);
return { success: false, error: error.message };
}
});
async function startServer() {
if (electronState.isDevelopment()) {
log.info('In dev mode, assuming external servers are running.');