Apollo GraphQL 之上传文件

前言
背景
在 Web 应用中,上传文件是非常常见的需求,而在使用 GraphQL 以后,应该如何实现上传文件的功能呢。
环境
- apollo-server:2.x;
- Altair:2.4.6;
后端
Apollo 直接提供了上传文件的功能,具体用法参见官方文档。
后端在处理时需要注意两个地方:
- 根据文档获取上传文件的文件名和 readable stream;
- 为了避免内存溢出,不用全部读取到内存,然后再保存文件,而是将 readable stream 通过管道直接连接到 writable stream;
# schema.graphql
type mutation {
uploadFile(file: Upload!): string!
}
// resolver.js
const mkdirp = require('mkdirp');
const localDir = './' // 当前文件夹
module.exports = {
Mutation: {
async uploadFile(_, { file }, ctx) {
const { createReadStream, filename } = await file;
const localFilename = uuidv1() + path.extname(filename);
const filePath = localDir + localFilename;
await mkdirp(localDir);
const writable = fs.createWriteStream(filePath);
await new Promise((resolve, reject) =>
createReadStream()
.pipe(writable)
.on('error', err => reject(err))
.on('close', resolve)
);
return filePath;
},
};
前端
客户端
cURL
后端写好以后不想再构建一个前端项目来测试,想直接使用 Postman 或者 Insomnia 之类的调试工具做调试,但是因为涉及到文件,暂时都不支持原生 GraphQL 的写法,只有通过原生 form-data
的方式请求。
后来在资料中找到直接使用 curl 的请求方式。
$ curl localhost:7001/graphql \
-F operations='{ "query": "mutation ($file: Upload!) { uploadFile(file: $file) }", "variables": { "file": null } }' \
-F map='{ "0": ["variables.file"] }' \
-F [email protected]
Altair
后来又在网上查找到了另外一款工具,叫做 Altair,算是为数不多的支持 Apollo 上传文件的客户端,语法也比较简单,直接在文件那里输入要替换的参数就可以了。由于我的把文件又封装了一层,试了一会才试出了封装一层的语法。Altair 提供了很多客户端,甚至包括浏览器的插件,为了方便,我就直接使用的 Web 端。
Vue
TODO