有没有人有一个如何使用GraphQl上传NestJs文件的例子?
我可以通过控制器使用给定的示例上传
https://github.com/nestjs/nest/issues/262#issuecomment-366098589,
但我找不到任何全面的文档如何在NestJS中使用GrahpQL上传
编辑:根据下面的Developia comment,apollo-server现在实现file upload。应该是首选方式。
下面,原始答案,供参考。
通常不使用GraphQL进行上传。 GraphQL是花哨的“API规范”,这意味着在一天结束时,低级HTTP请求和响应被转换为JSON对象/从JSON对象转换(如果您没有自定义传输)。
一种解决方案可能是在GraphQL架构中定义特殊端点,如:
mutation Mutation {
uploadFile(base64: String): Int
}
然后客户端将二进制数据转换为base64字符串,这将在解析器端相应地处理。这样,文件将成为GraphQL客户端和服务器之间交换的JSON对象的一部分。
虽然这可能适合小文件,少量操作,但绝对不是上传服务的解决方案。
在这个答案的时候,FileInterceptor
正在使用multer
并将ExecutionContext
转换为http
,它使用getRequest
和getResponse
方法为req
提供res
和multer.single
,它们是(req
和res
)在GraphQL中未定义。
我试图从上下文中获取请求:
const ctx = GqlExecutionContext.create(context);
在req
有ctx
财产,但我找不到使用multer
(尚)的方法。
无论如何,我对FileFieldsInterceptor
进行了一些更改,以便在我的项目中使用它,但是当我有时间清理它时,我可能会提出pull请求:
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
...
你可以使用apollo-upload-server lib。在我看来,这似乎是最简单的事情。干杯
您需要定义一个上传控制器并将其添加到您的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));