Next.js如何在API路由中获取上传图片并实现存储
前端代码问题修正
你当前写的前端上传代码有2个会直接导致上传失败的问题:
- FormData传参错误:
fd.append('myfile',image.name)只传了图片的文件名字符串,没有传入真实的文件对象,后端拿不到文件内容。这里第二个参数应该传入你从文件选择器拿到的完整File对象,而不是它的name属性。 - 请求头配置错误:手动设置
"Content-Type": "image/jpeg"会导致FormData上传失败。传输FormData时浏览器会自动生成带boundary分隔标识的Content-Type请求头,手动设置会覆盖这个值,导致后端无法正确解析表单内容,直接删掉这个headers配置即可。
修正后的前端代码参考:
const onSubmit = async(e) => { e.preventDefault(); const fd = new FormData() // 假设image是你从<input type="file">拿到的文件对象 fd.append('myfile', image) let res = await fetch(`http://localhost:3000/api/upload`,{ method: 'POST', // 不要手动设置Content-Type,浏览器自动处理 body: fd, }) let response = await res.json(); }
Next.js API 路由接收文件、保存到public文件夹实现
注意:这个方案仅适用于本地开发调试,生产环境禁止使用,因为Next.js构建部署后public文件夹是只读的,运行时写入的文件不会被对外访问,也会在实例重启/重新部署时丢失
分两种常用路由模式提供实现代码:
Pages Router 实现(pages/api/upload.js)
Next.js 原生支持FormData解析,不需要额外安装第三方解析库,配合node原生fs、path模块即可写入文件:
import fs from 'fs' import path from 'path' export const config = { api: { bodyParser: false, // 关闭默认body解析,兼容全版本formdata处理 }, } export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ message: '方法不允许' }) } try { const formData = await req.formData() const file = formData.get('myfile') if (!file) { return res.status(400).json({ message: '未接收到上传文件' }) } // 校验文件类型,只允许图片 if (!file.type.startsWith('image/')) { return res.status(400).json({ message: '仅支持上传图片文件' }) } // 读取文件二进制内容 const fileBuffer = Buffer.from(await file.arrayBuffer()) // 生成不重复的文件名,避免重名覆盖 const fileName = `${Date.now()}-${file.name.replace(/\s/g, '')}` // 拼接public文件夹下的存储路径 const savePath = path.join(process.cwd(), 'public', 'uploads', fileName) // 确保uploads文件夹存在,不存在则递归创建 const uploadDir = path.dirname(savePath) if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir, { recursive: true }) } // 写入文件到磁盘 fs.writeFileSync(savePath, fileBuffer) // 返回可直接访问的图片相对路径 return res.status(200).json({ message: '上传成功', imageUrl: `/uploads/${fileName}` }) } catch (error) { return res.status(500).json({ message: '上传失败', error: error.message }) } }
App Router 实现(app/api/upload/route.js)
App Router下的实现更简洁,不需要额外修改解析配置:
import fs from 'fs' import path from 'path' import { NextResponse } from 'next/server' export async function POST(req) { try { const formData = await req.formData() const file = formData.get('myfile') if (!file) { return NextResponse.json({ message: '未接收到上传文件' }, { status: 400 }) } if (!file.type.startsWith('image/')) { return NextResponse.json({ message: '仅支持上传图片文件' }, { status: 400 }) } const fileBuffer = Buffer.from(await file.arrayBuffer()) const fileName = `${Date.now()}-${file.name.replace(/\s/g, '')}` const savePath = path.join(process.cwd(), 'public', 'uploads', fileName) const uploadDir = path.dirname(savePath) if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir, { recursive: true }) } fs.writeFileSync(savePath, fileBuffer) return NextResponse.json({ message: '上传成功', imageUrl: `/uploads/${fileName}` }) } catch (error) { return NextResponse.json({ message: '上传失败', error: error.message }, { status: 500 }) } }
生产环境云存储方案
把上传文件存在public文件夹或者服务器本地磁盘完全不适合生产环境,正确的生产方案是使用专门的对象存储服务,通用实现逻辑如下:
- 核心原则:应用服务器不持久化存储用户上传的静态文件,避免部署、扩容、实例故障时文件丢失,也能降低应用服务器的带宽压力
- 实现步骤:
- 开通任意一家云厂商的对象存储服务,创建公共读权限的存储桶,配置好跨域规则允许你的前端域名访问
- 在Next.js API路由中接收到前端上传的文件对象后,不需要把文件写入本地磁盘,直接将文件流传给对应云服务的官方SDK
- SDK上传完成后会返回文件的公网访问地址,你可以把这个地址和其他业务数据一起存在自己的数据库里
- 前端需要展示图片时,直接读取数据库里存储的云存储URL即可
- 额外安全建议:上传时增加校验逻辑,限制文件大小(比如普通展示类图片限制最大2MB)、校验文件真实类型,避免恶意用户上传可执行文件、超大文件占用服务资源。
内容的提问来源于stack exchange,提问作者Vraj Solanki
相关产品推荐
相关产品推荐

