Apollo Express GraphQL文件上传Mutation前端调用返回404问题咨询
问题原因
- 核心错误是前端代码中
Dropzone返回的acceptedFiles是文件数组,你直接将整个数组传给了定义为单个Upload!类型的file变量,参数类型不匹配导致请求格式异常,触发404错误。 - 次要潜在问题:如果你的Apollo客户端同时配置了默认的
HttpLink,会和createUploadLink产生冲突,覆盖上传专用的请求逻辑。 - 额外隐性问题:你的服务端流写入的
await finished写法不符合异步规范,回调式写法无法被await捕获,会导致流还没写入完成就提前返回结果,也可能引发请求异常。
解决方法
1. 修复前端传参错误
修改Dropzone的onDrop回调,取数组第一个元素传给file变量,适配单文件上传的定义:
<Dropzone onDrop={acceptedFiles => { if(acceptedFiles?.length) { uploadImage({variables: {file: acceptedFiles[0]}}) } }}> {({getRootProps, getInputProps}) => ( <section> <div {...getRootProps()}> <input {...getInputProps()} /> <p>拖拽文件到此处,或点击选择文件上传</p> </div> </section> )} </Dropzone>
2. 确认Apollo客户端配置正确
确保没有额外配置HttpLink,createUploadLink是唯一的链路配置,避免链路冲突:
import { ApolloClient, InMemoryCache, createUploadLink } from '@apollo/client'; const client = new ApolloClient({ link: createUploadLink({ uri: 'http://localhost:4000/graphql' }), cache: new InMemoryCache(), });
3. 修复服务端流写入逻辑
将回调式的流写入改为Promise封装,确保await能正确等待流写入完成:
import {GraphQLUpload} from "graphql-upload" // ... Upload: GraphQLUpload, Mutation: { singleUpload: async (parent, {file, vacId, index}) => { const {createReadStream, filename, mimetype, encoding} = await file; const stream = createReadStream(); const nameToBe = `${vacId}-${index}.${filename.split('.').pop()}`; const location = path.join(__dirname, `../images/${nameToBe}`) const out = createWriteStream(location); // 改写为Promise封装的流写入逻辑 await new Promise((resolve, reject) => { stream.pipe(out) out.on('finish', resolve) out.on('error', (err) => { console.error("流写入失败", err) reject(err) }) }) return {filename, mimetype, encoding, url: `http://localhost:4000/static/${nameToBe}`}; } }
4. 确认服务端中间件顺序
你当前的中间件加载顺序是正确的,graphqlUploadExpress必须在server.applyMiddleware之前加载,不需要调整:
app.use(graphqlUploadExpress()); app.use('/static', express.static("./images")); server.applyMiddleware({app})
内容的提问来源于stack exchange,提问作者Nils
相关产品推荐
相关产品推荐

