非Apollo的Express GraphQL服务器文件上传报错:createReadStream不是函数
在无Apollo的Express GraphQL服务器中实现文件上传到AWS S3的解决方案
问题背景
使用Express搭建不含Apollo的GraphQL服务器,尝试将文件上传至AWS S3时,调用createReadStream出现错误:GraphQL Error: Error: File upload failed: createReadStream is not a function。环境为Node.js、Express、无Apollo的GraphQL、AWS S3,需解决文件上传的配置与正确实现问题。
核心问题分析
错误根源在于中间件顺序错误和请求解析流程冲突:
bodyParser.json()和bodyParser.urlencoded()会优先解析multipart/form-data类型请求,导致graphql-upload无法正确处理文件流。graphqlUploadExpress()中间件必须在任何解析请求体的中间件之前加载,否则无法捕获文件上传请求。
具体修复步骤
1. 调整Express中间件顺序
修改app.js中的中间件加载逻辑,将graphql-upload的中间件放在bodyParser之前,避免干扰文件上传请求解析:
// 先加载graphql-upload中间件,处理文件上传请求 app.use(graphqlUploadExpress({ maxFileSize: 10 * 1024 * 1024 })); // 限制单文件大小为10MB // 仅对非/graphql路由加载bodyParser app.use((req, res, next) => { if (req.path !== '/graphql') { bodyParser.json({ limit: '10mb' })(req, res, next); } else { next(); } }); app.use((req, res, next) => { if (req.path !== '/graphql') { bodyParser.urlencoded({ limit: '10mb', extended: true })(req, res, next); } else { next(); } });
2. 修复Resolver逻辑
当前Resolver直接传递file对象给上传函数,需先解析出文件流、文件名等信息:
const { uploadFile } = require("../../../../services/uploadFiles"); const { GraphQLUpload } = require('graphql-upload'); module.exports = { Upload: GraphQLUpload, Mutation: { discussionUploadFiles: async (_, { file }) => { try { // 解析file对象,获取流、文件名和MIME类型 const { createReadStream, filename, mimetype } = await file; console.log("filename", filename, "mimetype", mimetype); // 将文件流、文件名等传给上传函数 const fileUrl = await uploadFile(createReadStream(), filename, mimetype); console.log(fileUrl); return fileUrl; } catch (error) { throw new Error(`File upload failed: ${error.message}`); } } } };
3. 完善AWS S3上传函数
确保uploadFile函数正确处理文件流(标准实现参考):
const AWS = require('aws-sdk'); // 配置AWS S3客户端 const s3 = new AWS.S3({ accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, region: process.env.AWS_REGION }); exports.uploadFile = async (fileStream, filename, mimetype) => { const params = { Bucket: process.env.AWS_S3_BUCKET_NAME, Key: `uploads/${Date.now()}-${filename}`, // 生成唯一文件名避免冲突 Body: fileStream, ContentType: mimetype, ACL: 'public-read' // 根据需求设置文件访问权限 }; const uploadResult = await s3.upload(params).promise(); return uploadResult.Location; // 返回S3存储的文件URL };
4. 验证版本兼容性
确认依赖版本匹配:
graphql-upload@10.0.0要求graphql@14.0.0+和express@4.16.0+,你的package.json版本符合要求。- 避免同时使用
multer和graphql-upload处理GraphQL请求,两者会冲突。
客户端验证
你的client.js代码格式正确,需注意:
- 确保请求URL与服务器端口一致(当前客户端用3001,服务器默认4000,需对应调整)。
- 确认
formData.getHeaders()正确设置Content-Type为multipart/form-data。
内容的提问来源于stack exchange,提问作者Rajab Mohammadi
相关产品推荐
相关产品推荐

