如何将Axios POST请求返回的二进制流保存为文件?
Node.js调用VanceAI API保存文件损坏问题
我用Node.js、Axios和fs调用vanceai.com的API,保存后的文件损坏。相关代码如下:
export const imageDownloader = async (transId, name, ext) => { let data = qs.stringify({ 'api_token': process.env.VANCE_AI_API_KEY, 'trans_id': transId }); let config = { method: 'post', maxBodyLength: Infinity, url: 'https://api-service.vanceai.com/web_api/v1/download', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data : data }; await axios.request(config).then((response) => { let buff = Buffer.from(JSON.stringify(response.data)); fs.writeFile(`./finalImages/${name}.${ext}`, buff, () => console.log('finished downloading!')); }).catch((error) => { console.log(error); }); }
返回的文件内容片段如下:
"����\u0000\u0010JFIF\u0000\u0001\u0001\u0000\u0000\u0001\u0000\u0001\u0000\u0000��\u0000C\u0000\u0002\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0001\u0001\u0002\u0002\u0002\u0002\u0002\u0004\u0003\u0002\u0002\u0002\u0002\u0005\u0004\u0004\u0003\u0004\u0006\u0005\u0006\u0006\u0006\u0005\u0006\u0006\u0006\u0007\t\b\u0006\u0007\t\u0007\u0006\u0006\b\u000b\b\t\n\n\n\n\n\u0006\b\u000b\f\u000b\n\f\t\n\n\n��\u0000C\u0001\u0002\u0002\u0002\u0002\u0002\u0002\u0005\u0003\u0003\u0005\n\u0007\u0006\u0007\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n��\u0000\u0011\b\u0002�\u0005\u0010\u0003\u0001"\u0000\u0002\u0011\u0001\u0003\u0011\u0001��\u0000\u001f\u0000\u0000\u0001\u0005\u0001\u0001\u0001\u0001\u0001\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b��\u0000�\u0010\u0000\u0002\u0001\u0003\u0003\u0002\u0004\u0003\u0005\u0005\u0004\u0004\u0000\u0000\u0001}\u0001\u0002\u0003\u0000\u0004\u0011\u0005\u0012!1A\u0006\u0013Qa\u0007"q\u00142���\b#B��\u0015R��$3br�\t\n\u0016\u0017\u0018\u0019\u001a%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz���������������������������������������������������������������������������\u0000\u001f\u0001\u0000\u0003\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b��\u0000�\u0011\u0000\u0002\u0001\u0002\u0004\u0004\u0003\u0004\u0007\u0005\u0004\u0004\u0000\u0001\u0002w\u0000\u0001\u0002\u0003\u0011\u0004\u0005!1\u0006\u0012AQ\u0007aq\u0013"2�\b\u0014B����\t#3R�\u0015br�\n\u0016$4�%�\u0017\u0018\u0019\u001a&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz��������������������������������������������������������������������������\u0000\f\u0003\u0001\u0000\u0002\u0011\u0003\u0011\u0000?\u0000��a��\u001cT��� \u0003֭�j����c��׆�G�Z�\u001c? \u0014�\u0013\u0006�q@�pEM\u0015�NJ�N�h��9\u001cP֏�"�c� }ڐ[�u��'���ȵ8\u0004zո�\\�h�8Z��\t\u0015�a����\u0013l㰨�G��O_J������&٣Z\r\u0011��'� �\u00079�70�\u000f�)˼���I\b�c�i\u001c0\u0018�A`8�I��T\u0016BP�\u001f�V�\u0002�\u001c�\u0015l�=\u0001�ң`\u000br��\u0004[Q��|ۇ�R:\u001c�\u001d)��S�R\u001eO\u0002�{\u0002�"��x��H���Q��i29@\u0002i�)�H��\b��\u001d�\u001aJ�\u001cI\u0014|����bs�Vg8\u0018?�(\f@��TӸZĶ͊�85\b�:\u0003�R+8�x����\u001c����˷�5\f\u0003-�\u0013銐��\u0006��\u0005�\u000eE;sc<S@'���c�H��G5V�\u00101S�Y{Tn\u001b8�\u0017\u0018�\u0003�\u001a{\u0006\u0007\u0000�+\u0010@\n\nj@�~�4\u0000�RE#���H\u0014�4�\f\u000eE\u0001�\u001e29�:\u001e��[<\n\nk\u0013Ԋw\u0001�I\\�epz\u001cS˾9S�P%\u0000m*h���\u0019�N\u0014\u001al�A\u000bR\t\n\bo�֗%�\u0019搤$\n``` 我试过多种方法都没解决,还尝试用node-libcurl复制官方提供的curl请求,也没成功。官方curl示例: ```bash curl -X POST "https://api-service.vanceai.com/web_api/v1/download" \ -o /Users/vanceai/Downloads/enlarged-cat.jpg \ -d 'api_token=1234567890abcdefg' \ -d 'trans_id=6de4b562d1a01c3d2520608eae929646' \
注:我的响应日志和Postman里获取的一致。
问题原因
原代码核心错误:
- Axios默认会把响应解析为JSON,但API返回的是二进制图片数据,并非JSON格式
- 将解析后的JSON数据转成字符串再转Buffer写入文件,导致二进制数据被错误编码,最终文件损坏
修正后的代码
方法1:使用ArrayBuffer直接写入
设置Axios的responseType: 'arraybuffer',让它直接返回二进制数据,再写入文件:
import axios from 'axios'; import qs from 'qs'; import fs from 'fs/promises'; // 使用Promise版fs,避免回调嵌套 export const imageDownloader = async (transId, name, ext) => { const data = qs.stringify({ 'api_token': process.env.VANCE_AI_API_KEY, 'trans_id': transId }); const config = { method: 'post', maxBodyLength: Infinity, url: 'https://api-service.vanceai.com/web_api/v1/download', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: data, responseType: 'arraybuffer' // 关键:告诉Axios返回二进制数据 }; try { const response = await axios.request(config); await fs.writeFile(`./finalImages/${name}.${ext}`, response.data); console.log('finished downloading!'); } catch (error) { console.log(error); } }
方法2:使用Stream管道写入(适合大文件)
用Axios的流模式,直接把响应流管道到文件流,更高效:
import axios from 'axios'; import qs from 'qs'; import fs from 'fs'; import { pipeline } from 'stream/promises'; export const imageDownloader = async (transId, name, ext) => { const data = qs.stringify({ 'api_token': process.env.VANCE_AI_API_KEY, 'trans_id': transId }); const config = { method: 'post', maxBodyLength: Infinity, url: 'https://api-service.vanceai.com/web_api/v1/download', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: data, responseType: 'stream' // 设置为流模式 }; try { const response = await axios.request(config); const fileStream = fs.createWriteStream(`./finalImages/${name}.${ext}`); await pipeline(response.data, fileStream); console.log('finished downloading!'); } catch (error) { console.log(error); } }
关键修改点说明
responseType设置:必须指定为arraybuffer或stream,阻止Axios自动解析JSON- 直接使用
response.data:不需要做JSON.stringify或额外的Buffer转换,response.data本身就是二进制数据(ArrayBuffer)或流对象 - 使用Promise版fs:避免回调嵌套,确保
await能等待文件写入完成,原代码中await仅等待了Axios请求,未等待fs.writeFile完成
内容的提问来源于stack exchange,提问作者Joe Whitehorn
相关产品推荐
相关产品推荐

