Next.js项目在Vercel使用formidable上传文件报EROFS只读文件系统错误
问题根因
Vercel上的Serverless API函数运行在只读的沙箱文件系统中,不允许对打包后的项目目录执行写入操作。本地开发环境下,你可以把上传文件直接存在项目根目录下是因为本地文件系统可读写,但线上环境一旦尝试把文件落盘到项目目录,就会触发EROFS只读文件系统报错。
另外要明确:就算你绕过这个报错把文件存到某个目录,Vercel的Serverless沙箱是按请求隔离、临时清空的,不存在持久化的本地磁盘空间,你原来写的api/get.js想读取根目录文件的逻辑,在线上永远不会生效。
正确实现步骤
- 保留原有正确配置
你的api/upload.js里bodyParser: false的配置不能删除,Next.js默认会提前把请求体解析写入内存,关闭这个配置才能让formidable正常处理文件流。 - 调整formidable的临时存储路径
不要把上传目录设为项目相对路径./,改成Vercel允许的可写临时目录/tmp。这个目录是临时空间,仅在单次请求的生命周期内可用,请求结束后内容会自动清空,绝对不能作为永久存储使用。
也就是把原来的form.uploadDir = "./"替换为form.uploadDir = "/tmp",这样formidable写临时文件时就不会触碰只读的项目目录,从根源上解决EROFS报错。 - 即时转存临时文件
在formidable的解析回调中,拿到临时存放在/tmp的上传文件后,立刻读取文件内容,转存到独立的持久化存储中,转存完成后马上删除/tmp下的临时文件释放空间。
调整后的参考代码:
如果使用的是2.x以下版本的旧版formidable,把代码里的import formidable from 'formidable'; import fs from 'fs/promises'; export const config = { api: { bodyParser: false, }, }; export default async (req, res) => { const form = new formidable.IncomingForm(); form.uploadDir = "/tmp"; form.keepExtensions = true; form.parse(req, async (err, fields, files) => { if (err) { return res.status(500).send("文件解析失败"); } // 替换成你表单里实际的文件字段名 const uploadedFile = files.fileFieldName; // 读取/tmp下的临时文件内容 const fileBuffer = await fs.readFile(uploadedFile.filepath); // 在此处接入你的持久化存储逻辑,把fileBuffer存到可长期访问的存储中 // 存完及时删除临时文件 await fs.unlink(uploadedFile.filepath); res.send("上传完成"); }); };uploadedFile.filepath替换为uploadedFile.path即可。 - 移除无效的本地文件读取逻辑
原来的api/get.js中读取根目录文件列表的逻辑在线上没有实际作用,直接删除即可。Vercel的Serverless环境没有持久本地磁盘,即便临时存了文件,下一次请求进来时也无法读到上次请求留下的内容。
注意事项
- Vercel给Serverless函数提供的
/tmp临时目录总容量有限,不要长期占用,大文件上传建议直接做流中转,不要把整个文件读到内存中处理。 - 所有需要长期保存的文件,必须存在独立的持久化存储里,不要依赖Serverless函数的本地文件系统。
内容的提问来源于stack exchange,提问作者Rahul
相关产品推荐
相关产品推荐

