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

如何用Axios/Node.js的multipart/form-data PUT本地文件?

用Node.js + Axios实现PlayCanvas的CURL文件上传请求

原CURL请求

需要转换的可正常运行的CURL命令:

curl --request PUT \
  --url https://playcanvas.com/api/assets/ASSET_ID \
  --header 'Authorization: Bearer TOKEN_HERE' \
  --header 'Content-Type: multipart/form-data' \
  --form file=@/Users/ilia/Development/playcanvas-rivalry/files/test.script.js \
  --form branchId=BRANCH_ID

用户尝试的代码

const fs = require('fs')
const path = require('path')
const router = require('express').Router({ mergeParams: true })
const axios = require('axios')
const buildUrl = require('build-url')
const FormData = require('form-data')

router.put('/:id', async (req, res, next) => {
  const url = buildUrl(process.env.BASE_URL, {
    path: `/projects/${process.env.PROJECT_ID}/assets/${req.params.id}`,
  })

  const { file_name } = req.query
  const filePath = path.join(__dirname, `../files/${file_name}`)
  
  try {
    const formData = new FormData()
    formData.append('branchId', req.query.branchId || process.env.BRANCH_ID)
    formData.append('file', fs.readFile(filePath))
    
    const update = await axios.put(url, formData, {
      headers: {
        'Authorization': `Bearer ${process.env.TOKEN}`,
        'Content-Type': 'multipart/form-data',
        // ...formData.getHeaders()
      },
    })
    console.log(update);
    res.send(update)
  } catch (error) {
    res.send(error)
  }
})

用户认为问题出在fs.readFile,尝试过fs.createReadStream但无效,需解决如何用Node.js + Axios复制该CURL请求。

解决方案

错误分析

  1. fs.readFile异步问题:fs.readFile是异步方法,直接传入会返回Promise而非文件内容,无法被FormData正确解析;即使改用fs.createReadStream,错误的头部设置也会导致请求失败。
  2. Content-Type头部错误:手动设置'Content-Type: multipart/form-data'会丢失FormData自动生成的boundary分隔符,服务端无法解析表单数据。
  3. URL路径不匹配:原CURL的API路径是/api/assets/ASSET_ID,用户代码中路径为/projects/${PROJECT_ID}/assets/${id},不符合PlayCanvas API的实际路径要求。

修正后的代码

const fs = require('fs')
const path = require('path')
const router = require('express').Router({ mergeParams: true })
const axios = require('axios')
const buildUrl = require('build-url')
const FormData = require('form-data')

router.put('/:id', async (req, res, next) => {
  // 修正URL路径,匹配原CURL的API结构
  const url = buildUrl(process.env.BASE_URL, {
    path: `/api/assets/${req.params.id}`,
  })

  const { file_name, branchId } = req.query
  const filePath = path.join(__dirname, `../files/${file_name}`)
  
  try {
    const formData = new FormData()
    // 添加branchId字段
    formData.append('branchId', branchId || process.env.BRANCH_ID)
    // 使用流式上传文件,同时指定文件名(和CURL的@效果一致)
    formData.append('file', fs.createReadStream(filePath), {
      filename: path.basename(filePath)
    })
    
    const update = await axios.put(url, formData, {
      headers: {
        'Authorization': `Bearer ${process.env.TOKEN}`,
        // 合并FormData自动生成的头部(包含正确的boundary)
        ...formData.getHeaders()
      },
    })
    
    res.status(200).json(update.data)
  } catch (error) {
    // 返回详细错误信息便于调试
    res.status(error.response?.status || 500).json({
      message: error.message,
      details: error.response?.data || '无详细错误信息'
    })
  }
})

关键修正点

  • 用fs.createReadStream替代fs.readFile,流式上传文件并指定filename,确保服务端识别文件名。
  • 移除手动设置的Content-Type,改用...formData.getHeaders()让FormData自动生成包含boundary的正确头部。
  • 修正URL路径为原CURL的/api/assets/ASSET_ID结构,匹配PlayCanvas API要求。
  • 优化错误处理,返回更详细的错误信息,方便调试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 02:36:31