如何从React应用向自建Node.js服务器上传嵌套文件夹?
实现React到自建Node.js服务器的嵌套文件夹上传
核心问题分析
你之前的multer代码只提取了文件名(originalname),丢失了文件的嵌套路径信息。要保留文件夹结构,关键是获取文件的相对路径并在后端重建目录。
解决方案:直接上传带路径的文件(推荐)
1. 前端React实现
利用HTML5的webkitdirectory属性让文件选择器支持文件夹选择,每个选中的文件会携带webkitRelativePath字段,包含完整的嵌套路径。
import { useState } from 'react'; const FolderUploader = () => { const [uploadStatus, setUploadStatus] = useState(''); const handleFolderUpload = async (e) => { const files = Array.from(e.target.files); const formData = new FormData(); // 同时传递文件和对应的相对路径 files.forEach((file, index) => { formData.append('files', file); formData.append(`relativePaths[${index}]`, file.webkitRelativePath); }); try { const response = await fetch('http://localhost:3000/upload-folder', { method: 'POST', body: formData, }); setUploadStatus(response.ok ? '文件夹上传成功,结构已保留' : '上传失败,请重试'); } catch (err) { setUploadStatus(`上传出错:${err.message}`); } }; return ( <div> <input type="file" webkitdirectory onChange={handleFolderUpload} multiple /> <p>{uploadStatus}</p> </div> ); }; export default FolderUploader;
2. 后端Node.js修改
使用fs-extra替代原生fs,它支持递归创建目录和可靠的文件移动,同时通过前端传递的relativePaths重建嵌套结构。
首先安装依赖:
npm install express multer fs-extra
后端代码:
const express = require('express'); const multer = require('multer'); const path = require('path'); const fs = require('fs-extra'); const app = express(); // 配置multer临时存储目录 const upload = multer({ dest: 'tmp/uploads/' }); app.post('/upload-folder', upload.array('files'), async (req, res) => { try { const files = req.files; const relativePaths = req.body.relativePaths; if (!files.length || !relativePaths) { return res.status(400).send('未收到文件或路径信息'); } // 遍历文件,重建目录结构 for (let i = 0; i < files.length; i++) { const file = files[i]; const targetPath = path.join('server/folder', relativePaths[i]); // 递归创建目标目录(自动创建父目录) await fs.ensureDir(path.dirname(targetPath)); // 移动临时文件到目标路径(支持跨磁盘移动,覆盖已存在文件) await fs.move(file.path, targetPath, { overwrite: true }); } res.send('文件夹上传成功,嵌套结构已保留'); } catch (err) { console.error('上传处理错误:', err); res.status(500).send(`服务器处理出错:${err.message}`); } }); app.listen(3000, () => { console.log('服务器运行在端口3000'); });
备选方案:Zip打包上传
如果担心多文件上传的网络稳定性,可以先将文件夹打包为Zip上传,后端解压后直接存储到目标目录,不影响视频的随时访问(解压是一次性操作,之后文件直接存放在服务器目录中)。
前端打包(使用jszip)
安装依赖:
npm install jszip
前端代码片段:
import JSZip from 'jszip'; const handleZipUpload = async (e) => { const files = Array.from(e.target.files); const zip = new JSZip(); // 将文件按相对路径添加到Zip包 files.forEach(file => { zip.file(file.webkitRelativePath, file); }); // 生成Zip Blob const zipBlob = await zip.generateAsync({ type: 'blob' }); const formData = new FormData(); formData.append('zipFile', zipBlob, 'uploaded-folder.zip'); // 发送请求(逻辑同之前的上传) try { const response = await fetch('http://localhost:3000/upload-zip', { method: 'POST', body: formData, }); // 处理响应... } catch (err) { // 处理错误... } };
后端解压(使用unzipper)
安装依赖:
npm install unzipper
后端代码:
const unzipper = require('unzipper'); app.post('/upload-zip', upload.single('zipFile'), async (req, res) => { try { // 解压Zip包到目标目录 await fs.createReadStream(req.file.path) .pipe(unzipper.Extract({ path: 'server/folder' })) .promise(); // 删除临时Zip文件 await fs.remove(req.file.path); res.send('Zip文件夹上传并解压成功'); } catch (err) { console.error('解压错误:', err); res.status(500).send('解压失败,请重试'); } });
内容的提问来源于stack exchange,提问作者el pipila
相关产品推荐
相关产品推荐

