Загрузка NestJS с использованием GraphQL
У кого-нибудь есть пример того, как загрузить файл в NestJs с помощью GraphQl?
Я могу загрузить, используя данный пример через контроллер
https://github.com/nestjs/nest/issues/262,
но я не смог найти исчерпывающую документацию по загрузке с помощью GrahpQL в NestJS
7 ответов
Apollo Server 2.0 должен иметь возможность сделать это сейчас (упакован в гнездо), хотя мне нужно было установить graphql-upload
и импорт GraphQLUpload
поскольку я не мог найти Upload
тип:
@Mutation(() => Image, { nullable: true })
async addImage(@Args({ name: 'image', type: () => GraphQLUpload }) image) {
// Do stuff with image...
}
На момент этого ответа FileInterceptor
использует multer
и путем преобразования ExecutionContext
в http
оно использует getRequest
а также getResponse
методы для обеспечения req
а также res
в multer.single
которые они есть (req
а также res
) не определено в GraphQL.
Я попытался получить запрос из контекста, используя:
const ctx = GqlExecutionContext.create(context);
и есть req
недвижимость в ctx
но я не могу найти способ использовать 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
...
Эта реализация отлично работает с Node> = v14.
- package.json
Удалите записи fs-конденсатор и 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 (пример преобразователя)
Удалите импорт GraphQLUpload из apollo-server-core и вместо этого импортируйте из 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
Некоторые другие ссылки, которые я нашел полезными:
- https://stephen-knutter.github.io/2020-02-07-nestjs-graphql-file-upload/
- Для загрузки файлов с помощью Почтальона Ссылка
Попробуй это
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))
);
}
}
РЕДАКТИРОВАТЬ: Согласно комментарию Developia ниже, apollo-server теперь осуществляет загрузку файлов. Должен быть предпочтительным способом.
Ниже оригинальный ответ, для справки.
Обычно для загрузки не используется GraphQL. GraphQL - это модная "спецификация API", означающая, что в конце дня низкоуровневый HTTP-запрос и ответы транслируются в / из объектов JSON (если у вас нет пользовательского транспорта).
Одним из решений может быть определение специальной конечной точки в схеме GraphQL, например:
mutation Mutation {
uploadFile(base64: String): Int
}
Затем клиент преобразует двоичные данные в строку base64, что будет обрабатываться соответствующим образом на стороне распознавателя. Таким образом, файл станет частью объекта JSON, которым обмениваются клиент и сервер GraphQL.
Хотя это может подходить для небольших файлов, небольшого количества операций, это определенно не решение для службы загрузки.
Вам необходимо определить контроллер загрузки и добавить его в свой 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('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-upload-server. Похоже, что проще всего сделать, на мой взгляд. ура