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

非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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 15:28:13