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

Next.js/React客户端文件夹上传实现方案咨询

在React/Next.js中实现客户端文件夹上传的可行方案

一、原生React实现(无需第三方库)

利用HTML5标准的directory属性(搭配webkitdirectory做兼容)即可实现,核心是通过webkitRelativePath获取文件的层级结构:

'use client';
import { useState } from 'react';

export default function NativeFolderUpload() {
  const [folderFiles, setFolderFiles] = useState([]);

  const handleFolderChange = (e) => {
    const files = Array.from(e.target.files);
    // 通过webkitRelativePath拿到文件在文件夹中的完整相对路径
    const structuredFiles = files.map(file => ({
      name: file.name,
      relativePath: file.webkitRelativePath,
      rawFile: file
    }));
    setFolderFiles(structuredFiles);
  };

  return (
    <div>
      <input
        type="file"
        directory=""
        webkitdirectory=""
        onChange={handleFolderChange}
        multiple
      />
      <h3>选中的文件及路径:</h3>
      <ul>
        {folderFiles.map((item, idx) => (
          <li key={idx}>{item.relativePath}</li>
        ))}
      </ul>
    </div>
  );
}

二、react-dropzone 实现拖拽文件夹上传

react-dropzone已原生支持文件夹上传,只需配置directory: true选项:

'use client';
import { useDropzone } from 'react-dropzone';

export default function DropzoneFolderUpload() {
  const { getRootProps, getInputProps, acceptedFiles } = useDropzone({
    directory: true,
    multiple: true
  });

  const structuredFiles = acceptedFiles.map(file => ({
    name: file.name,
    relativePath: file.webkitRelativePath,
    rawFile: file
  }));

  return (
    <div {...getRootProps()} style={{ border: '2px dashed #ccc', padding: '2rem', borderRadius: '8px' }}>
      <input {...getInputProps()} />
      <p>拖拽文件夹到此处,或点击选择文件夹</p>
      <h3>选中内容:</h3>
      <ul>
        {structuredFiles.map((item, idx) => (
          <li key={idx}>{item.relativePath}</li>
        ))}
      </ul>
    </div>
  );
}

注:需先安装依赖:npm install react-dropzone

三、用JSZip打包文件夹后上传

如果需要将整个文件夹打包为Zip再上传,可通过JSZip结合文件相对路径构建层级:

'use client';
import { useState } from 'react';
import JSZip from 'jszip';

export default function FolderZipUpload() {
  const [zipBlob, setZipBlob] = useState(null);

  const handleFolderSelect = async (e) => {
    const files = Array.from(e.target.files);
    const zip = new JSZip();

    // 根据相对路径创建文件夹层级并添加文件
    files.forEach(file => {
      const pathSegments = file.webkitRelativePath.split('/');
      const folderPath = pathSegments.slice(0, -1).join('/');
      if (folderPath) {
        zip.folder(folderPath).file(file.name, file);
      } else {
        zip.file(file.name, file);
      }
    });

    // 生成Zip文件Blob
    const generatedZip = await zip.generateAsync({ type: 'blob' });
    setZipBlob(generatedZip);
  };

  const uploadZip = () => {
    if (!zipBlob) return;
    const formData = new FormData();
    formData.append('folderZip', new File([zipBlob], 'uploaded-folder.zip', { type: 'application/zip' }));
    
    // 此处替换为你的上传接口
    fetch('/api/upload-folder', {
      method: 'POST',
      body: formData
    }).then(res => res.json()).then(console.log);
  };

  return (
    <div>
      <input
        type="file"
        directory=""
        webkitdirectory=""
        onChange={handleFolderSelect}
      />
      {zipBlob && <button onClick={uploadZip}>上传Zip包</button>}
    </div>
  );
}

注:需先安装依赖:npm install jszip

常见问题排查

  • 确保组件是Next.js客户端组件(添加'use client'指令),服务端组件无法调用浏览器文件API;
  • 浏览器兼容性:directory属性支持Chrome 21+、Firefox 50+、Edge 13+,老旧浏览器可能无法正常工作;
  • 若之前实现失败,大概率是未正确使用webkitRelativePath获取文件层级,该属性是还原文件夹结构的核心。

内容的提问来源于stack exchange,提问作者Divyesh Bhuva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 16:16:14