使用graphql-upload实现Node.js客户端向GraphQL接口上传文件问题咨询
Node.js 客户端Apollo GraphQL文件上传解决方案
错误原因排查
你的两个实现方案都存在破坏二进制文件内容的问题,对应错误点如下:
- 第一个方案错误
- 调用
fs.createReadStream时指定了encoding: "utf8",会将二进制图片文件按UTF-8规则转成字符串,破坏原始二进制结构,直接导致文件大小翻倍、内容损坏 - 普通GraphQL请求客户端默认不支持
GraphQLUpload规范的多部分请求格式,直接传递流不会自动完成form-data结构拼接
- 调用
- 第二个方案错误
执行streamToString将文件流转为字符串的操作同样破坏了原始二进制内容,上传的不是完整的图片文件,自然无法正常解析使用
正确实现方案
遵循Apollo GraphQL上传规范,直接传递原始二进制读流到formData即可,无需做任何编码转换:
依赖安装(如未安装)
npm i form-data axios graphql
完整代码实现
const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); const path = require('path'); const { print, gql } = require('graphql'); // 定义上传Mutation const UPLOAD = gql` mutation UploadFile($file: Upload!) { uploadFile(file: $file) } `; async function upload(GRAPHQL_URL, apiKey) { const formData = new FormData(); // 配置GraphQL请求参数 formData.append( "operations", JSON.stringify({ query: print(UPLOAD), variables: { file: null } }) ); // 配置文件到variables的映射关系 formData.append( "map", JSON.stringify({ "0": ["variables.file"], }) ); // *注意:创建读流时不要添加encoding配置,默认输出二进制Buffer流* const fileStream = fs.createReadStream(path.join(__dirname, "image.png")); // 追加文件,手动指定文件名和mimetype,避免服务端识别异常 formData.append("0", fileStream, { filename: "image.png", contentType: "image/png" }); // 发送请求 const { data } = await axios.post(GRAPHQL_URL, formData, { headers: { "X-API-KEY": apiKey, // 必须合并formData自动生成的请求头,包含multipart分隔符配置 ...formData.getHeaders() }, // 可选:大文件上传时放开大小和超时限制 maxBodyLength: Infinity, timeout: 60000 }); return data; }
核心注意事项
- 所有操作不要修改原始二进制流,禁止将流转为字符串、指定非默认编码等操作
- 必须手动指定文件的
filename和contentType,避免服务端解析时丢失mimetype - 请求头必须包含
form-data自动生成的multipart/form-data类型和boundary参数,否则服务端无法正常解析请求体
内容的提问来源于stack exchange,提问作者Maxim
相关产品推荐
相关产品推荐

