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

使用fetch() POST上传PDF报415 Unsupported Media Type错误求助

使用fetch()上传PDF至HubSpot API遇415/400错误的解决办法

问题情况

用fetch()上传PDF到HubSpot API时,一直碰到415 Unsupported Media Type错误。PDF和JS文件在同一目录,文件名没问题。试了各种Content-Type设置:

  • 设为"multipart/form-data"返回400错误;
  • 设为"application/json"或"text/plain"返回415错误;
  • 删掉Content-Type头还是返回400错误。

用request.post()能成功上传,但想换成fetch()实现,当前代码如下:

async function uploadFile(filePath, extension, timestamp) {

    const url = "https://api.hubapi.com/files/v3/files";
    // const url = "https://api.hubapi.com/filemanager/api/v3/files/upload"; // also doesn't work.
    var filename = `${filePath}.${extension}`;

    var fileOptions = {
        access: 'PRIVATE',
        overwrite: false,
        duplicateValidationStrategy: 'NONE',
        duplicateValidationScope: 'ENTIRE_PORTAL'
    };

    var formData = {
        file: fs.createReadStream(filename),
        fileName: `${filename} (${timestamp}).${extension}`,
        options: JSON.stringify(fileOptions),
        folderPath: 'Quotations'
    };

    try {
        const response = await fetch(url, { 
            "method": "POST",
            "formData": formData,
            "headers": {
                'Authorization': `Bearer ${process.env.ACCESS_TOKEN}`,
                "Content-Type": "application/pdf"
            }
        });

        if(!response.ok) {
            throw new Error(`Error. Response not ok: ${response.status}`);
        }
        
        const data = await response.json();
        return data;

    } catch(error) {
        console.log(`Error: ${error}`);
    }

}

const fileId = uploadFile("Quotation", "pdf", getCurrentTimestamp());
fileId.then((data) => console.log(data));

问题原因

  1. Node.js环境下的fetch(不管是原生还是node-fetch)不支持直接传普通对象当formData,必须用标准FormData构造请求体;
  2. 手动设置Content-Type: application/pdf会覆盖multipart/form-data的正确请求头,导致HubSpot API无法解析请求结构;
  3. 文件流的传递方式不对,没有正确告知API文件的类型和名称。

修改后的代码

先确保安装依赖:

npm install form-data node-fetch

修改后的上传函数:

const fetch = require('node-fetch');
const FormData = require('form-data');
const fs = require('fs');

async function uploadFile(filePath, extension, timestamp) {
    const url = "https://api.hubapi.com/files/v3/files";
    const filename = `${filePath}.${extension}`;
    const newFileName = `${filename} (${timestamp}).${extension}`;

    const fileOptions = {
        access: 'PRIVATE',
        overwrite: false,
        duplicateValidationStrategy: 'NONE',
        duplicateValidationScope: 'ENTIRE_PORTAL'
    };

    // 构造标准FormData实例
    const formData = new FormData();
    // 附加文件流,指定文件名和PDF类型
    formData.append('file', fs.createReadStream(filename), {
        filename: newFileName,
        contentType: 'application/pdf'
    });
    formData.append('fileName', newFileName);
    formData.append('options', JSON.stringify(fileOptions));
    formData.append('folderPath', 'Quotations');

    try {
        const response = await fetch(url, { 
            method: "POST",
            body: formData, // 用body传递FormData,不是自定义的formData字段
            headers: {
                'Authorization': `Bearer ${process.env.ACCESS_TOKEN}`,
                // 用FormData自动生成的请求头,包含正确的boundary
                ...formData.getHeaders()
            }
        });

        if (!response.ok) {
            // 获取API返回的错误详情,方便排查
            const errorInfo = await response.text();
            throw new Error(`请求失败: ${response.status} - ${errorInfo}`);
        }
        
        const data = await response.json();
        return data;

    } catch(error) {
        console.error(`错误: ${error.message}`);
    }
}

// 补充时间戳函数示例
function getCurrentTimestamp() {
    return new Date().toISOString().replace(/[:.]/g, '-');
}

const uploadPromise = uploadFile("Quotation", "pdf", getCurrentTimestamp());
uploadPromise.then((data) => console.log(data)).catch(err => console.error(err));

关键修改点

  • 用form-data库创建标准的FormData对象,这是Node.js下fetch处理文件上传的正确方式;
  • 附加文件时指定文件名和application/pdf类型,让HubSpot能识别文件格式;
  • 请求体用body: formData传递,原代码的formData字段是错误的;
  • 不要手动设置Content-Type,formData.getHeaders()会自动生成包含boundary的multipart/form-data头,这是解决400/415错误的核心;
  • 捕获错误时获取响应文本,能看到API返回的具体错误信息,方便调试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 10:12:51