Nestjs 中的 Graphql Apollo 上传返回无效值
Posted
技术标签:
【中文标题】Nestjs 中的 Graphql Apollo 上传返回无效值【英文标题】:Graphql Apollo upload in Nestjs returns invalid value 【发布时间】:2020-05-20 10:21:42 【问题描述】:我尝试使用 graphql-upload
的 GraphQLUpload
标量向 GraphQL 端点添加上传参数:
import FileUpload, GraphQLUpload from 'graphql-upload'
@Mutation(() => Image, nullable: true )
async addImage(@Args(name: 'image', type: () => GraphQLUpload) image: FileUpload): Promise<Image | undefined>
// do stuff...
这最初是有效的。然而,运行几次后,它开始返回以下错误:
"Variable \"$image\" got invalid value ; Expected type Upload. Upload value invalid."
尝试使用 Insomnia 客户端和 curl 进行测试:
curl localhost:8000/graphql \
-F operations=' "query": "mutation ($image: Upload!) addImage(image: $image) id ", "variables": "image": null '
-F map=' "0": ["variables.image"] '
-F 0=@/path/to/image
【问题讨论】:
@xadm 感谢您的回复。这里的规范:github.com/jaydenseric/graphql-multipart-request-spec 提到这些应该为空,不是这样吗? 它工作... node.js 错误/警告?重启? @xadmGraphQLError: Upload value invalid. at GraphQLScalarType.parseValue (/path/to/project/nest/node_modules/graphql-upload/lib/GraphQLUpload.js:66:11)
。它最初对我有用(大约一个小时),然后出现错误,我不确定发生了什么变化。
【参考方案1】:
使用import GraphQLUpload from "apollo-server-express"
不从'graphql-upload'导入GraphQLUpload
【讨论】:
为什么这个答案不在顶部?! 我在 docker 上遇到了问题,这对我有用。你来自未来【参考方案2】:根据@willsquire 的回答,我意识到由于某种原因,Scalar 装饰器对我不起作用,因此我最终将 graphql-upload 替换为下一个 sn-p
import * as FileType from 'file-type'
import GraphQLError, GraphQLScalarType from 'graphql'
import Readable from 'stream'
export interface FileUpload
filename: string
mimetype: string
encoding: string
createReadStream: () => Readable
export const GraphQLUpload = new GraphQLScalarType(
name: 'Upload',
description: 'The `Upload` scalar type represents a file upload.',
async parseValue(value: Promise<FileUpload>): Promise<FileUpload>
const upload = await value
const stream = upload.createReadStream()
const fileType = await FileType.fromStream(stream)
if (fileType?.mime !== upload.mimetype)
throw new GraphQLError('Mime type does not match file content.')
return upload
,
parseLiteral(ast): void
throw new GraphQLError('Upload literal unsupported.', ast)
,
serialize(): void
throw new GraphQLError('Upload serialization unsupported.')
,
)
【讨论】:
抱歉回复晚了@Mutation(() => File) async uploadFile( @Args( name: 'input', type: () => GraphQLUpload ) fileInput: FileUpload, ): Promise<File> const url = await this.filesService.create(fileInput) return url, success: true
GraphQLUpload 和 FileUpload 是从我之前的代码中导入的,文件类型只是您要返回的架构
上传文件时出现此错误:RangeError: Maximum call stack size exceeded at ReadStream.open"【参考方案3】:
对我来说,@Yhozen 和@Willsquire 在这里提出的解决方案是一种解决方法,但不是问题的真正答案。
就我而言,真正的问题来自 graphql-upload
我的依赖项中有它,它正在创建此堆栈中描述的错误。
通过消除依赖,它解决了问题。正如@willsquire 评论的那样,graphql-upload 已经在 apollo-server 包中,无需将其导入包中。
【讨论】:
它现在可能已经暴露了,但当时类型还没有暴露(即FileUpload
)。因此依赖和导入:)
当然@willsquire。你是 2 月 12 日的评论指导我。谢谢你。在尝试解决问题几个小时后,您给了我权利提示!【参考方案4】:
经过一番挖掘,apollo-server-core
似乎自动解析中间件中的文件上传,graphql-upload
基于请求是多部分形式,而不是通过标量类型名称确定。所以 graphql-upload
不一定需要,因为它已经集成,但它对于获取解析的类型很有用:
import Scalar from '@nestjs/graphql'
import FileType from 'file-type'
import GraphQLError from 'graphql'
import FileUpload from 'graphql-upload'
import isUndefined from 'lodash'
@Scalar('Upload')
export class Upload
description = 'File upload scalar type'
async parseValue(value: Promise<FileUpload>)
const upload = await value
const stream = upload.createReadStream()
const fileType = await FileType.fromStream(stream)
if (isUndefined(fileType)) throw new GraphQLError('Mime type is unknown.')
if (fileType?.mime !== upload.mimetype)
throw new GraphQLError('Mime type does not match file content.')
return upload
2021 年 1 月 2 日更新
今天仍在为此苦苦挣扎。这里有一些很好的答案,但它们不再对我有用。问题是如果我在parseValue
中抛出错误,它会“挂起”。以下解决方案通过解决“挂起”问题并仍然推送实际文件以供使用(用例是.csv
文件)对我来说效果最好:
import UnsupportedMediaTypeException from '@nestjs/common'
import Scalar from '@nestjs/graphql'
import ValueNode from 'graphql'
import FileUpload, GraphQLUpload from 'graphql-upload'
export type CSVParseProps =
file: FileUpload
promise: Promise<FileUpload>
export type CSVUpload = Promise<FileUpload | Error>
export type CSVFile = FileUpload
@Scalar('CSV', () => CSV)
export class CSV
description = 'CSV upload type.'
supportedFormats = ['text/csv']
parseLiteral(arg: ValueNode)
const file = GraphQLUpload.parseLiteral(arg, (arg as any).value)
if (
file.kind === 'ObjectValue' &&
typeof file.filename === 'string' &&
typeof file.mimetype === 'string' &&
typeof file.encoding === 'string' &&
typeof file.createReadStream === 'function'
)
return Promise.resolve(file)
return null
// If this is `async` then any error thrown
// hangs and doesn't return to the user. However,
// if a non-promise is returned it fails reading the
// stream later. We can't evaluate the `sync`
// version of the file either as there's a data race (it's not
// always there). So we return the `Promise` version
// for usage that gets parsed after return...
parseValue(value: CSVParseProps)
return value.promise.then((file) =>
if (!this.supportedFormats.includes(file.mimetype))
return new UnsupportedMediaTypeException(
`Unsupported file format. Supports: $this.supportedFormats.join(
' '
).`
)
return file
)
serialize(value: unknown)
return GraphQLUpload.serialize(value)
这个在ArgsType
:
@Field(() => CSV)
file!: CSVUpload
解析器中的这个:
// returns either the file or error to throw
const fileRes = await file
if (isError(fileRes)) throw fileRes
【讨论】:
您能否进一步解释一下您是如何使您的突变发挥作用的?我遇到了同样的错误 你的apollo-server-core
包最晚了吗?如果是这样,它的依赖项中包含graphql-upload
,并且已经有中间件处理文件上传。【参考方案5】:
如果你想使用 graphql-upload 包,那么你必须应用他们的 Express 中间件,并禁用 apollo 服务器的内部上传模块。
看到这个答案: https://***.com/a/64659256/10404270
之后,像这样的突变对我有用:
import GraphQLUpload, FileUpload from "graphql-upload";
@Mutation(() => Boolean)
async docUpload(
@Arg('userID') userid: number,
@Arg('file', () => GraphQLUpload)
file: FileUpload
)
const filename, createReadStream = file;
console.log(userid, file, filename, createReadStream);
return true
【讨论】:
【参考方案6】:最佳答案在这里: https://dilushadasanayaka.medium.com/nestjs-file-upload-with-grapql-18289b9e32a2
注意:要测试文件上传,您必须发送正确格式的文件。 Graphql 不支持文件路径。如:
file: '/user/mim/desktop/t.txt'
您必须收到完整的文件。
【讨论】:
以上是关于Nestjs 中的 Graphql Apollo 上传返回无效值的主要内容,如果未能解决你的问题,请参考以下文章
通过 Apollo Server (NestJS) 处理异常
elastic APM 和 NestJS 不区分 graphql 查询