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

Heroku部署Node.js服务时base64转文件功能报错如何解决

故障原因

代码本地运行正常、Heroku部署后失效,核心是3个代码问题+1个平台特性不匹配:

  • Heroku采用临时只读文件系统:除/tmp系统临时目录外,其余所有路径应用都没有写入权限,直接在项目工作目录写入文件会触发EACCES权限报错;且dyno重启、重新部署时/tmp外的所有文件都会被重置,平台本身也不支持在项目目录持久化存储文件。
  • 同步API用法错误:writeFileSync是同步阻塞方法,不支持传入回调函数,原有代码里写的错误回调永远不会执行,一旦写入失败错误会直接抛出,回调逻辑完全捕获不到异常。
  • 未处理base64前缀:前端上传的base64通常带data:mime;base64,格式的头,如果本地测试用的是纯base64字符串、线上传输带了前缀,转出来的Buffer会是损坏的,后续上传IPFS会直接失败。
  • 异常分支资源泄漏:如果文件写入成功但后续上传IPFS报错,unlinkSync逻辑不会执行,临时文件会残留,高并发下容易占满磁盘空间。
修复方案

最优方案(完全规避文件系统问题)

web3.storage的put方法本身支持直接传入File/Blob/Buffer对象,完全不需要先写本地文件再读取,从根源上避开Heroku的文件系统限制,性能更高:

import { File } from '@web-std/file'

export const uploadFile = async(req, res) => {
    try {
        // 移除base64的data头
        const pureBase64 = req.body.file.replace(/^data:.+;base64,/, '')
        const fileBuffer = Buffer.from(pureBase64, 'base64')
        // 直接构造File对象,无需落盘
        const uploadFile = new File([fileBuffer], req.body.filename)
        const rootCid = await client.put([uploadFile])
        const info = await client.status(rootCid)
        res.status(200).json(`${info.cid}.ipfs.dweb.link/${req.body.filename}`)
    } catch (error) {
        res.status(500).json(error)
    }
}

兼容原有落盘逻辑的修复

如果必须走本地文件读写的流程,把文件写入路径改到系统临时目录,修正同步API的用法,加资源兜底清理:

import { writeFileSync, unlinkSync, existsSync } from 'fs'
import { tmpdir } from 'os'
import { join } from 'path'

export const uploadFile = async(req, res) => {
    const pureBase64 = req.body.file.replace(/^data:.+;base64,/, '')
    const fileBuffer = Buffer.from(pureBase64, 'base64')
    // 拼接系统临时目录的文件路径,禁止写入项目工作目录
    const tempFilePath = join(tmpdir(), req.body.filename)
    
    try {
        // 同步方法不需要传回调,错误会自动被catch捕获
        writeFileSync(tempFilePath, fileBuffer)
        const file = await getFilesFromPath(tempFilePath)
        const rootCid = await client.put(file)
        const info = await client.status(rootCid)
        res.status(200).json(`${info.cid}.ipfs.dweb.link/${req.body.filename}`)
    } catch (error) {
        res.status(500).json(error)
    } finally {
        // 无论流程成功失败,都兜底删除临时文件
        if (existsSync(tempFilePath)) {
            unlinkSync(tempFilePath)
        }
    }
}
部署注意事项
  • Heroku的/tmp目录单实例有存储空间上限,单dyno临时空间通常在500MB-1GB左右,不要存储超过限制的大文件,用完必须及时清理。
  • 不要依赖/tmp目录做持久化存储,dyno每24小时会自动重启一次,重启后临时目录的所有内容都会被清空。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:18:19