Node.js使用got.stream上传流至form-data时遭遇411长度要求错误
解决Storyblock API(S3底层)外部URL流上传的411 Length Required错误
本地文件用createReadStream上传到Storyblock API完全正常,但用got.stream拉取外部URL上传时,会返回411 Length Required错误。核心原因是S3(以及基于它的Storyblock API)要求请求必须携带Content-Length头,而两种流的特性存在关键差异:
createReadStream返回的是文件系统流,自带文件大小元数据,form-data库能自动读取并生成正确的Content-Length请求头;got.stream返回的是HTTP请求流,没有内置的文件大小信息,form-data无法自动计算整个请求的总长度,导致请求缺少必要头触发错误。
解决方案一:提前获取文件大小,手动指定给form-data
先发送HEAD请求拿到远程文件的Content-Length,再在追加流时传给form-data的knownLength参数:
import FormData from 'form-data'; import got from 'got'; async function uploadFromExternalUrl(externalUrl, apiEndpoint) { // 发送HEAD请求获取文件大小 const headRes = await got.head(externalUrl, { decompress: false }); const fileSize = parseInt(headRes.headers['content-length'], 10); const form = new FormData(); // 追加流时指定已知长度 form.append('file', got.stream(externalUrl, { decompress: false }), { knownLength: fileSize }); // 提交表单到API const submitRes = await form.submit(apiEndpoint); console.log('上传成功,状态码:', submitRes.statusCode); } // 调用示例 uploadFromExternalUrl('https://example.com/remote-file.jpg', 'YOUR_STORYBLOCK_UPLOAD_ENDPOINT') .catch(err => console.error('上传失败:', err));
解决方案二:先下载远程文件到本地临时文件,再上传
如果远程服务器不支持HEAD请求,或者文件体积不大,可以先把文件下载到本地临时文件,再用熟悉的本地流方式上传:
import { createReadStream, createWriteStream } from 'fs'; import { pipeline } from 'stream/promises'; import FormData from 'form-data'; import got from 'got'; import { tmpdir } from 'os'; import { join } from 'path'; async function uploadFromExternalUrl(externalUrl, apiEndpoint) { // 创建临时文件路径 const tempFile = join(tmpdir(), `temp-upload-${Date.now()}.jpg`); // 下载远程文件到临时文件 await pipeline( got.stream(externalUrl, { decompress: false }), createWriteStream(tempFile) ); // 用本地文件流上传 const form = new FormData(); form.append('file', createReadStream(tempFile)); const submitRes = await form.submit(apiEndpoint); console.log('上传成功,状态码:', submitRes.statusCode); // 可选:清理临时文件 // await fs.promises.unlink(tempFile); } // 调用示例 uploadFromExternalUrl('https://example.com/remote-file.jpg', 'YOUR_STORYBLOCK_UPLOAD_ENDPOINT') .catch(err => console.error('上传失败:', err));
内容的提问来源于stack exchange,提问作者Christophvh
相关产品推荐
相关产品推荐

