You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 15:50:24