Apollo Server GraphQL 文件上传异常:响应无限缓冲且文件为空
既然你已经定位到问题出在mutation和schema文件里,那咱们重点排查这两块的常见错误,帮你解决响应无限缓冲和生成空文件的问题:
一、Schema 文件的常见问题
未正确定义
Upload标量
Apollo Server 2.x不会自动包含Upload标量,你需要显式在schema中声明,并且在resolver中绑定对应的实现:# 必须添加这个标量定义 scalar Upload type Mutation { # 参数类型必须是Upload!,确保文件是必填的 uploadFile(file: Upload!): String! }Mutation字段参数类型错误
如果你把file的类型写成了String或者其他非Upload类型,会导致无法正确接收文件流,直接引发后续的写入错误。
二、Mutation Resolver 的关键错误点
这部分是空文件和无限缓冲的重灾区,主要是对Upload类型的异步处理不到位:
没有await
Upload对象Upload类型本质是一个Promise,直接使用file而不await它,会导致你拿到的不是包含文件流的对象,而是一个未解析的Promise,自然读不到任何数据。未等待文件流写入完成
如果你只是调用stream.pipe(writeStream)就直接返回响应,Node.js的流是异步的,此时文件还没写完就结束请求,会导致生成空文件,同时服务器可能因为未处理完流操作而陷入缓冲状态。缺少错误捕获机制
流操作过程中如果出现错误(比如权限不足、磁盘空间不够),没有捕获的话会导致服务器挂起,出现无限缓冲的情况。
正确的Resolver示例
const { createWriteStream } = require('fs'); const { join } = require('path'); const { GraphQLUpload } = require('graphql-upload'); const resolvers = { // 必须绑定Upload标量的实现 Upload: GraphQLUpload, Mutation: { async uploadFile(_, { file }) { try { // 必须await file来获取文件的实际数据 const { createReadStream, filename, mimetype } = await file; const stream = createReadStream(); // 确保images文件夹存在(可以提前手动创建,或者用fs.mkdirSync判断) const filePath = join(__dirname, 'images', filename); // 用Promise包裹流操作,等待写入完成 await new Promise((resolve, reject) => { const writeStream = createWriteStream(filePath); stream.pipe(writeStream); // 监听完成和错误事件 writeStream.on('finish', () => resolve(`File saved: ${filename}`)); writeStream.on('error', (err) => { console.error('File write error:', err); reject(new Error('Failed to save file')); }); }); return `File uploaded successfully: ${filename}`; } catch (err) { console.error('Upload error:', err); throw new Error('Upload failed. Please try again.'); } } } };
三、额外的配置检查
除了schema和resolver,还要确保Apollo Server启用了文件上传中间件:
const express = require('express'); const { ApolloServer } = require('apollo-server-express'); const { graphqlUploadExpress } = require('graphql-upload'); const app = express(); // 添加文件上传中间件,设置最大文件大小等限制 app.use(graphqlUploadExpress({ maxFileSize: 10 * 1024 * 1024, maxFiles: 1 })); const server = new ApolloServer({ typeDefs, resolvers }); server.applyMiddleware({ app }); app.listen(4000, () => console.log('Server running on http://localhost:4000/graphql'));
总结
你的问题大概率是因为没有await Upload对象,或者没有等待文件流写入完成就返回响应,导致服务器无法正确结束请求(无限缓冲),同时文件没有被完整写入(空文件)。按照上面的示例调整schema和resolver,应该就能解决问题了。
内容的提问来源于stack exchange,提问作者user14645831

