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

求NextJS 13.3基于App Router的本地文件上传最简示例

NextJS 13.3 本地文件上传最简实现

以下是基于NextJS 13.3+ App Router特性的完整可运行方案,包含前端上传表单和后端API路由处理:

1. 前端上传表单(客户端组件)

创建 app/upload/page.tsx,标记为客户端组件以处理交互逻辑:

'use client';

import { useState } from 'react';

export default function UploadPage() {
  const [file, setFile] = useState<File | null>(null);
  const [uploadStatus, setUploadStatus] = useState<string | null>(null);

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files?.[0]) {
      setFile(e.target.files[0]);
    }
  };

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (!file) return;

    setUploadStatus('上传中...');
    const formData = new FormData();
    formData.append('file', file);

    try {
      const res = await fetch('/api/upload', {
        method: 'POST',
        body: formData,
      });

      if (res.ok) {
        const data = await res.json();
        setUploadStatus(`上传成功!文件可访问路径:${data.filePath}`);
        setFile(null);
      } else {
        setUploadStatus('上传失败,请重试');
      }
    } catch (err) {
      setUploadStatus('上传出错:' + (err as Error).message);
    }
  };

  return (
    <div style={{ maxWidth: 600, margin: '2rem auto', padding: '0 1rem' }}>
      <h1>文件上传</h1>
      <form onSubmit={handleSubmit} encType="multipart/form-data">
        <div style={{ marginBottom: '1rem' }}>
          <input
            type="file"
            onChange={handleFileChange}
            required
            style={{ padding: '0.5rem' }}
          />
        </div>
        <button
          type="submit"
          disabled={!file}
          style={{
            padding: '0.5rem 1rem',
            backgroundColor: '#0070f3',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer',
          }}
        >
          上传文件
        </button>
      </form>
      {uploadStatus && <p style={{ marginTop: '1rem' }}>{uploadStatus}</p>}
    </div>
  );
}

2. 后端API路由处理

创建 app/api/upload/route.ts,用于接收并存储文件:
首先安装依赖:

npm install formidable @types/formidable

路由代码:

import { NextResponse } from 'next/server';
import formidable from 'formidable';
import fs from 'fs/promises';
import path from 'path';

// 禁用NextJS默认的body解析,交由formidable处理
export const config = {
  api: {
    bodyParser: false,
  },
};

// 指定使用Node.js运行时(Edge Runtime不支持文件系统操作)
export const runtime = 'nodejs';

export async function POST(request: Request) {
  const form = formidable({
    uploadDir: path.join(process.cwd(), 'public/uploads'),
    keepExtensions: true, // 保留原始文件扩展名
  });

  try {
    // 自动创建uploads目录(不存在时)
    await fs.mkdir(path.join(process.cwd(), 'public/uploads'), { recursive: true });

    // 解析表单数据
    const { files } = await new Promise<{ fields: formidable.Fields; files: formidable.Files }>((resolve, reject) => {
      form.parse(request, (err, fields, files) => {
        if (err) reject(err);
        else resolve({ fields, files });
      });
    });

    const uploadedFile = files.file as formidable.File;
    if (!uploadedFile) {
      return NextResponse.json({ error: '未选择文件' }, { status: 400 });
    }

    // 返回前端可直接访问的文件路径
    const filePath = `/uploads/${path.basename(uploadedFile.filepath)}`;
    return NextResponse.json({ success: true, filePath });
  } catch (err) {
    return NextResponse.json({ error: (err as Error).message }, { status: 500 });
  }
}

3. 关键配置与注意事项

  • 文件会存储在项目根目录的 public/uploads 文件夹,可通过 http://localhost:3000/uploads/[文件名] 直接访问
  • 可在formidable配置中添加限制规则:
    formidable({
      uploadDir: path.join(process.cwd(), 'public/uploads'),
      keepExtensions: true,
      maxFileSize: 5 * 1024 * 1024, // 限制最大5MB
      filter: (part) => part.mimetype?.startsWith('image/') || part.mimetype?.startsWith('application/pdf'), // 限制文件类型
    });
    
  • 生产环境建议使用云存储(如AWS S3)替代本地存储,Vercel等平台不支持持久化本地文件
  • 若需调整最大请求大小,修改 next.config.js:
    /** @type {import('next').NextConfig} */
    const nextConfig = {
      serverRuntimeConfig: {
        maxRequestBodySize: '5mb',
      },
    };
    
    module.exports = nextConfig;
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 03:24:56