GraphQL 是一种用于 API 的查询语言和运行时环境,它可以轻松地处理复杂的数据关系和查询。但是,当我们需要在 GraphQL 中实现文件上传和下载时,可能会遇到一些挑战。本文将介绍如何在 GraphQL 中实现文件上传和下载,并提供示例代码。
文件上传
在 GraphQL 中实现文件上传需要使用 GraphQLUpload 类型。这个类型是由 graphql-upload 库提供的,它是一个用于处理文件上传的 GraphQL 扩展。
安装 graphql-upload
首先,我们需要安装 graphql-upload 库:
npm install graphql-upload
在 schema 中定义上传类型
接下来,我们需要在 schema 中定义上传类型。使用 GraphQLUpload 类型,我们可以定义一个 Upload 类型,它表示上传的文件。
scalar Upload
type Mutation {
uploadFile(file: Upload!): Boolean
}实现文件上传的 resolver
现在,我们需要实现一个 resolver 来处理文件上传。我们可以使用 createReadStream 方法来读取上传的文件,并将其保存到服务器上。
// www.javascriptcn.com code example
const { createWriteStream } = require('fs');
const { createReadStream } = require('fs');
const { GraphQLUpload } = require('graphql-upload');
const resolvers = {
Mutation: {
uploadFile: async (parent, { file }) => {
const { createReadStream, filename } = await file;
const stream = createReadStream();
const path = `${__dirname}/uploads/${filename}`;
return new Promise((resolve, reject) =>
stream
.pipe(createWriteStream(path))
.on('finish', () => resolve(true))
.on('error', () => reject(false))
);
},
},
Upload: GraphQLUpload,
};
module.exports = resolvers;这个 resolver 接受一个 file 参数,使用 createReadStream 方法读取上传的文件,并将其保存到服务器上。最后,它返回一个 Promise,如果文件上传成功,则 Promise 将被解析为 true,否则将被拒绝为 false。
在客户端上传文件
现在,我们已经在服务器上实现了文件上传,我们需要在客户端上实现上传文件的功能。我们可以使用 fetch 方法来发送一个 multipart/form-data 请求,并将上传的文件作为 FormData 对象发送。
// www.javascriptcn.com code example
const fileInput = document.querySelector('#file-input');
fileInput.addEventListener('change', async () => {
const file = fileInput.files[0];
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/graphql', {
method: 'POST',
headers: {
Accept: 'application/json',
},
body: formData,
});
const result = await response.json();
console.log(result);
});这个代码片段监听一个 <input type="file"> 元素的变化事件,当用户选择一个文件时,它将使用 fetch 方法将文件上传到服务器。注意,我们需要将请求的 Content-Type 设置为 multipart/form-data。
文件下载
在 GraphQL 中实现文件下载需要使用 GraphQLScalarType 类型。这个类型是由 GraphQL 原生提供的,它可以表示任何标量值。
在 schema 中定义下载类型
首先,我们需要在 schema 中定义下载类型。使用 GraphQLScalarType 类型,我们可以定义一个 Download 类型,它表示下载的文件。
scalar Download
type Query {
downloadFile(filename: String!): Download
}实现文件下载的 resolver
现在,我们需要实现一个 resolver 来处理文件下载。我们可以使用 createReadStream 方法来读取服务器上的文件,并将其作为响应的一部分发送给客户端。
// www.javascriptcn.com code example
const { createReadStream } = require('fs');
const { GraphQLScalarType } = require('graphql');
const resolvers = {
Query: {
downloadFile: (parent, { filename }, context, info) => {
const path = `${__dirname}/uploads/${filename}`;
const stream = createReadStream(path);
return new Promise((resolve, reject) => {
const chunks = [];
stream.on('data', chunk => chunks.push(chunk));
stream.on('error', error => reject(error));
stream.on('end', () => {
const buffer = Buffer.concat(chunks);
resolve(buffer.toString('base64'));
});
});
},
},
Download: new GraphQLScalarType({
name: 'Download',
description: 'The `Download` scalar type represents a base64-encoded file.',
parseValue: value => value,
serialize: value => value,
parseLiteral: ast => ast.value,
}),
};
module.exports = resolvers;这个 resolver 接受一个 filename 参数,使用 createReadStream 方法读取服务器上的文件,并将其作为响应的一部分发送给客户端。最后,它返回一个 Promise,Promise 将被解析为一个 base64 编码的字符串,表示下载的文件。
在客户端下载文件
现在,我们已经在服务器上实现了文件下载,我们需要在客户端上实现下载文件的功能。我们可以使用 fetch 方法来发送一个请求,并将响应的 base64 编码字符串解码为一个二进制文件。
// www.javascriptcn.com code example
const downloadButton = document.querySelector('#download-button');
downloadButton.addEventListener('click', async () => {
const filename = 'test.txt';
const response = await fetch(`/graphql?query={downloadFile(filename:"${filename}")}`);
const result = await response.json();
const fileContent = atob(result.data.downloadFile);
const blob = new Blob([fileContent], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
});这个代码片段创建一个 <a> 元素,当用户点击一个按钮时,它将使用 fetch 方法从服务器下载文件。注意,我们需要将查询字符串包含在 URL 中,并将响应的 base64 编码字符串解码为一个二进制文件。最后,我们使用 createObjectURL 方法创建一个 URL,将其作为下载链接的 href 属性,并模拟点击下载链接。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d8f145a941bf7134054a21