React+Node.js上传文件至SFTP服务器失败问题求助
解决SFTP文件上传问题的方案
问题根源拆解
- 直接传
inputFile后端收空对象:前端未用FormData封装文件,axios默认以JSON格式发送,无法正确传递二进制文件数据。 - DataURL方式上传失败:
ssh2-sftp-client的put方法不支持DataURL格式,且你当前代码中remoteFilePath设为目录/,但put要求指定具体文件名,同时DataURL包含的前缀(如data:image/png;base64,)未处理。
方案一:用FormData直接上传文件(推荐,体积更小)
前端代码修改
调整文件上传逻辑,用FormData封装文件和参数:
async function handleDockerInput(e) { const inputFile = e.target.files[0]; try { const loginResponse = await authService.logIn(user); console.log('Login Response: ', loginResponse); if (inputFile) { const formData = new FormData(); // 添加文件 formData.append('file', inputFile); // 注意:生产环境别直接传SFTP配置,建议后端存配置,前端传配置ID避免泄密 formData.append('connect', JSON.stringify(SFTPConfig)); // 传远程目录(后续后端拼接文件名) formData.append('remoteDir', '/'); const response = await dockerService.putObject(formData); console.log(response); } else { alert('请选择要上传的文件'); } } catch (error) { console.error('登录失败: ', error); } }
修改dockerService.putObject,axios会自动处理multipart/form-data的请求头:
async putObject(formData) { const response = await axios.post('/docker/upload', formData); return response; }
后端代码修改
- 先安装文件解析中间件
multer:
npm install multer
- 配置multer并改造
upload函数:
import multer from 'multer'; // 用内存存储文件,避免存本地直接上传SFTP const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); // 路由中需先通过multer解析文件,示例: // router.post('/docker/upload', upload.single('file'), yourController.upload); public async upload(req: Request, res: Response): Promise<void> { // 获取上传的文件Buffer和原文件名 const fileBuffer = req.file?.buffer; const fileName = req.file?.originalname; if (!fileBuffer || !fileName) { res.status(400).send('未上传有效文件'); return; } // 解析SFTP配置和远程目录 const connectConfig = JSON.parse(req.body.connect); const remoteDir = req.body.remoteDir; // 拼接完整远程文件路径(目录+文件名) const remoteFilePath = `${remoteDir}${fileName}`; const sftp = new Client(); const sftpConfig: SftpConfig = { host: connectConfig.host, port: connectConfig.port, username: connectConfig.username, password: connectConfig.password, }; try { await sftp.connect(sftpConfig); // 先检查远程目录是否存在 const dirExists = await sftp.exists(remoteDir); if (!dirExists) { res.status(400).send('远程目录不存在'); return; } // 上传Buffer到SFTP await sftp.put(fileBuffer, remoteFilePath); console.log('文件上传成功'); res.status(200).send('文件上传成功'); } catch (error) { console.error('SFTP上传错误:', error); res.status(500).send('上传文件到SFTP失败'); } finally { await sftp.end(); // 确保连接关闭 } }
方案二:修复DataURL上传方式(不推荐,文件体积增大30%)
前端补充修改
在请求体中添加文件名:
const body = JSON.stringify({ connect: SFTPConfig, filepath: dataUrl, remoteFilePath: '/', fileName: inputFile.name // 新增原文件名 });
后端代码修改
解码DataURL为Buffer,并指定远程文件名:
public async upload(req: Request, res: Response): Promise<void> { const dataUrl = req.body.filepath; const remoteDir = req.body.remoteFilePath; const fileName = req.body.fileName; if (!dataUrl || !fileName) { res.status(400).send('缺少DataURL或文件名'); return; } // 去除DataURL前缀,解码为Buffer const base64Data = dataUrl.replace(/^data:.+;base64,/, ''); const fileBuffer = Buffer.from(base64Data, 'base64'); const remoteFilePath = `${remoteDir}${fileName}`; const sftp = new Client(); const { host, port, username, password } = req.body.connect; const sftpConfig: SftpConfig = { host: host, port: port, username: username, password: password, }; try { await sftp.connect(sftpConfig); const dirExists = await sftp.exists(remoteDir); if (!dirExists) { res.status(400).send('远程目录不存在'); return; } await sftp.put(fileBuffer, remoteFilePath); console.log('文件上传成功'); res.status(200).send('文件上传成功'); } catch (error) { console.error('SFTP上传错误:', error); res.status(500).send('上传文件到SFTP失败'); } finally { await sftp.end(); } }
关键注意事项
- 安全风险:绝对不要在前端传递SFTP账号密码,建议后端存储配置,前端仅传递配置ID。
- 错误排查:后端要打印完整错误信息(如
console.error(error)),不要只返回自定义提示,方便定位问题。 - 路径要求:
ssh2-sftp-client的put方法必须指定具体文件名,不能只传目录。
内容的提问来源于stack exchange,提问作者Alessandro
相关产品推荐
相关产品推荐

