NestJS 使用 GraphQL 上传
NestJS upload using GraphQL
有没有人有如何使用 GraphQl 在 NestJs 中上传文件的示例?
我可以通过控制器使用给定的示例上传
https://github.com/nestjs/nest/issues/262#issuecomment-366098589,
但我找不到任何关于如何在 NestJS 中使用 GrahpQL 上传的综合文档
编辑:根据 below, apollo-server now implements file upload。应该是首选方式。
以下为原答案,供参考
人们通常不会使用 GraphQL 进行上传。 GraphQL 很花哨 "specification of API",这意味着在一天结束时,低级 HTTP 请求和响应被翻译成 to/from JSON 个对象(如果您没有自定义传输)。
一种解决方案是在 GraphQL 架构中定义特殊端点,例如:
mutation Mutation {
uploadFile(base64: String): Int
}
然后客户端会将二进制数据转换为base64字符串,这将在解析器端进行相应处理。这样,文件将成为 GraphQL 客户端和服务器之间交换的 JSON 对象的一部分。
虽然这可能适用于小文件、少量操作,但绝对不是上传服务的解决方案。
您可以使用 apollo-upload-server 库。在我看来,这似乎是最容易做的事情。干杯
在回答这个问题时 FileInterceptor
使用 multer
并且通过将 ExecutionContext
转换为 http
它使用 getRequest
和 getResponse
向 multer.single
提供 req
和 res
的方法,它们在 GraphQL 中未定义(req
和 res
)。
我尝试使用以下方法从上下文中获取请求:
const ctx = GqlExecutionContext.create(context);
ctx
中有 req
属性 但我找不到使用 multer
的方法(还)。
无论如何,我对 FileFieldsInterceptor
进行了一些更改以在我的项目中使用它,但是当我有时间清理它时我可能会提出拉取请求:
import { Observable } from 'rxjs';
import {
NestInterceptor,
Optional,
ExecutionContext,
mixin,
} from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { storeFile } from './storeFile';
interface IField {
name: string;
options?: any;
}
export function GraphqlFileFieldsInterceptor(
uploadFields: IField[],
localOptions?: any,
) {
class MixinInterceptor implements NestInterceptor {
options: any = {};
constructor(@Optional() options: any = {}) {
this.options = { ...options, ...localOptions };
}
async intercept(
context: ExecutionContext,
call$: Observable<any>,
): Promise<Observable<any>> {
const ctx = GqlExecutionContext.create(context);
const args = ctx.getArgs();
let storeFilesResult = await Promise.all(
uploadFields.map(uploadField => {
const file = args[uploadField.name];
return storeFile(file, {
...uploadField.options,
...this.options,
}).then(address => {
args[uploadField.name] = address;
return address;
});
}),
);
return call$;
}
}
const Interceptor = mixin(MixinInterceptor);
return Interceptor;
}
存储文件是这样的(可能不能这样使用):
import uuid from 'uuid/v4';
import fs from 'fs';
import path from 'path';
const dir = './files';
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
export const storeFile = async (file, options): Promise<any> => {
// options is not doing anything right now
const { stream } = await file;
const filename = uuid();
const fileAddress = path.join(dir, filename + '.jpg');
return new Promise((resolve, reject) =>
stream
.on('error', error => {
if (stream.truncated)
// Delete the truncated file
fs.unlinkSync(fileAddress);
reject(error);
})
.pipe(fs.createWriteStream(fileAddress))
.on('error', error => reject(error))
.on('finish', () => resolve(fileAddress)),
);
};
在我的 Cats.resolvers.ts
:
...
@Mutation()
@UseInterceptors(
GraphqlFileFieldsInterceptor([
{ name: 'catImage1' },
{ name: 'catImage2' },
{ name: 'catImage3' },
]),
)
async cats(
@Args('catImage1') catImage1: string,
@Args('catImage2') catImage2: string,
@Args('catImage3') catImage3: string,
){
console.log(catImage1) // will print catImage1 address
...
您需要定义一个上传控制器并将其添加到您的 app.module,这是控制器应该是什么的示例(后端):
@Controller()
export class Uploader {
@Post('sampleName')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file) {
// file name selection
const path = `desired path`;
const writeStream = fs.createWriteStream(path);
writeStream.write(file.buffer);
writeStream.end();
return {
result: [res],
};
}
}
并在前端通过 fetch 调用您的控制器:
fetch('controller address', {
method: 'POST',
body: data,
})
.then((response) => response.json())
.then((success) => {
// What to do when succeed
});
})
.catch((error) => console.log('Error in uploading file: ', error));
Apollo Server 2.0 现在应该可以做到这一点(打包在 nest 中),尽管我需要安装 graphql-upload
并导入 GraphQLUpload
,因为我找不到 Upload
类型:
@Mutation(() => Image, { nullable: true })
async addImage(@Args({ name: 'image', type: () => GraphQLUpload }) image) {
// Do stuff with image...
}
试试这个
import { Resolver, Mutation, Args } from '@nestjs/graphql';
import { createWriteStream } from 'fs';
import {GraphQLUpload} from "apollo-server-express"
@Resolver('Download')
export class DownloadResolver {
@Mutation(() => Boolean)
async uploadFile(@Args({name: 'file', type: () => GraphQLUpload})
{
createReadStream,
filename
}): Promise<boolean> {
return new Promise(async (resolve, reject) =>
createReadStream()
.pipe(createWriteStream(`./uploads/${filename}`))
.on('finish', () => resolve(true))
.on('error', () => reject(false))
);
}
}
此实现与 Node >= v14 完美配合
- package.json
如果您添加了 fs-capacitor 和 graphql-upload 条目,请从解决方案部分中删除它们,并安装最新版本的 graphql-upload(此时为 v11.0.0)包作为依赖项。
- src/app.module.ts
禁用 Apollo Server 的内置上传处理并将 graphqlUploadExpress 中间件添加到您的应用程序。
import { graphqlUploadExpress } from "graphql-upload"
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common"
@Module({
imports: [
GraphQLModule.forRoot({
uploads: false, // disable built-in upload handling
}),
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(graphqlUploadExpress()).forRoutes("graphql")
}
}
- src/blog/post.resolver.ts(解析器示例)
从 apollo-server-core 中删除 GraphQLUpload 导入,改为从 graphql-upload 导入
import { FileUpload, GraphQLUpload } from "graphql-upload"
@Mutation(() => Post)
async postCreate(
@Args("title") title: string,
@Args("body") body: string,
@Args("attachment", { type: () => GraphQLUpload }) attachment: Promise<FileUpload>,
) {
const { filename, mimetype, encoding, createReadStream } = await attachment
console.log("attachment:", filename, mimetype, encoding)
const stream = createReadStream()
stream.on("data", (chunk: Buffer) => /* do stuff with data here */)
}
来源: https://github.com/nestjs/graphql/issues/901#issuecomment-780007582
我发现有帮助的其他一些链接:
有没有人有如何使用 GraphQl 在 NestJs 中上传文件的示例?
我可以通过控制器使用给定的示例上传
https://github.com/nestjs/nest/issues/262#issuecomment-366098589,
但我找不到任何关于如何在 NestJS 中使用 GrahpQL 上传的综合文档
编辑:根据
以下为原答案,供参考
人们通常不会使用 GraphQL 进行上传。 GraphQL 很花哨 "specification of API",这意味着在一天结束时,低级 HTTP 请求和响应被翻译成 to/from JSON 个对象(如果您没有自定义传输)。
一种解决方案是在 GraphQL 架构中定义特殊端点,例如:
mutation Mutation {
uploadFile(base64: String): Int
}
然后客户端会将二进制数据转换为base64字符串,这将在解析器端进行相应处理。这样,文件将成为 GraphQL 客户端和服务器之间交换的 JSON 对象的一部分。
虽然这可能适用于小文件、少量操作,但绝对不是上传服务的解决方案。
您可以使用 apollo-upload-server 库。在我看来,这似乎是最容易做的事情。干杯
在回答这个问题时 FileInterceptor
使用 multer
并且通过将 ExecutionContext
转换为 http
它使用 getRequest
和 getResponse
向 multer.single
提供 req
和 res
的方法,它们在 GraphQL 中未定义(req
和 res
)。
我尝试使用以下方法从上下文中获取请求:
const ctx = GqlExecutionContext.create(context);
ctx
中有 req
属性 但我找不到使用 multer
的方法(还)。
无论如何,我对 FileFieldsInterceptor
进行了一些更改以在我的项目中使用它,但是当我有时间清理它时我可能会提出拉取请求:
import { Observable } from 'rxjs';
import {
NestInterceptor,
Optional,
ExecutionContext,
mixin,
} from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { storeFile } from './storeFile';
interface IField {
name: string;
options?: any;
}
export function GraphqlFileFieldsInterceptor(
uploadFields: IField[],
localOptions?: any,
) {
class MixinInterceptor implements NestInterceptor {
options: any = {};
constructor(@Optional() options: any = {}) {
this.options = { ...options, ...localOptions };
}
async intercept(
context: ExecutionContext,
call$: Observable<any>,
): Promise<Observable<any>> {
const ctx = GqlExecutionContext.create(context);
const args = ctx.getArgs();
let storeFilesResult = await Promise.all(
uploadFields.map(uploadField => {
const file = args[uploadField.name];
return storeFile(file, {
...uploadField.options,
...this.options,
}).then(address => {
args[uploadField.name] = address;
return address;
});
}),
);
return call$;
}
}
const Interceptor = mixin(MixinInterceptor);
return Interceptor;
}
存储文件是这样的(可能不能这样使用):
import uuid from 'uuid/v4';
import fs from 'fs';
import path from 'path';
const dir = './files';
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
export const storeFile = async (file, options): Promise<any> => {
// options is not doing anything right now
const { stream } = await file;
const filename = uuid();
const fileAddress = path.join(dir, filename + '.jpg');
return new Promise((resolve, reject) =>
stream
.on('error', error => {
if (stream.truncated)
// Delete the truncated file
fs.unlinkSync(fileAddress);
reject(error);
})
.pipe(fs.createWriteStream(fileAddress))
.on('error', error => reject(error))
.on('finish', () => resolve(fileAddress)),
);
};
在我的 Cats.resolvers.ts
:
...
@Mutation()
@UseInterceptors(
GraphqlFileFieldsInterceptor([
{ name: 'catImage1' },
{ name: 'catImage2' },
{ name: 'catImage3' },
]),
)
async cats(
@Args('catImage1') catImage1: string,
@Args('catImage2') catImage2: string,
@Args('catImage3') catImage3: string,
){
console.log(catImage1) // will print catImage1 address
...
您需要定义一个上传控制器并将其添加到您的 app.module,这是控制器应该是什么的示例(后端):
@Controller()
export class Uploader {
@Post('sampleName')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file) {
// file name selection
const path = `desired path`;
const writeStream = fs.createWriteStream(path);
writeStream.write(file.buffer);
writeStream.end();
return {
result: [res],
};
}
}
并在前端通过 fetch 调用您的控制器:
fetch('controller address', {
method: 'POST',
body: data,
})
.then((response) => response.json())
.then((success) => {
// What to do when succeed
});
})
.catch((error) => console.log('Error in uploading file: ', error));
Apollo Server 2.0 现在应该可以做到这一点(打包在 nest 中),尽管我需要安装 graphql-upload
并导入 GraphQLUpload
,因为我找不到 Upload
类型:
@Mutation(() => Image, { nullable: true })
async addImage(@Args({ name: 'image', type: () => GraphQLUpload }) image) {
// Do stuff with image...
}
试试这个
import { Resolver, Mutation, Args } from '@nestjs/graphql';
import { createWriteStream } from 'fs';
import {GraphQLUpload} from "apollo-server-express"
@Resolver('Download')
export class DownloadResolver {
@Mutation(() => Boolean)
async uploadFile(@Args({name: 'file', type: () => GraphQLUpload})
{
createReadStream,
filename
}): Promise<boolean> {
return new Promise(async (resolve, reject) =>
createReadStream()
.pipe(createWriteStream(`./uploads/${filename}`))
.on('finish', () => resolve(true))
.on('error', () => reject(false))
);
}
}
此实现与 Node >= v14 完美配合
- package.json
如果您添加了 fs-capacitor 和 graphql-upload 条目,请从解决方案部分中删除它们,并安装最新版本的 graphql-upload(此时为 v11.0.0)包作为依赖项。
- src/app.module.ts
禁用 Apollo Server 的内置上传处理并将 graphqlUploadExpress 中间件添加到您的应用程序。
import { graphqlUploadExpress } from "graphql-upload"
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common"
@Module({
imports: [
GraphQLModule.forRoot({
uploads: false, // disable built-in upload handling
}),
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(graphqlUploadExpress()).forRoutes("graphql")
}
}
- src/blog/post.resolver.ts(解析器示例)
从 apollo-server-core 中删除 GraphQLUpload 导入,改为从 graphql-upload 导入
import { FileUpload, GraphQLUpload } from "graphql-upload"
@Mutation(() => Post)
async postCreate(
@Args("title") title: string,
@Args("body") body: string,
@Args("attachment", { type: () => GraphQLUpload }) attachment: Promise<FileUpload>,
) {
const { filename, mimetype, encoding, createReadStream } = await attachment
console.log("attachment:", filename, mimetype, encoding)
const stream = createReadStream()
stream.on("data", (chunk: Buffer) => /* do stuff with data here */)
}
来源: https://github.com/nestjs/graphql/issues/901#issuecomment-780007582
我发现有帮助的其他一些链接: