使用 NestJS 上传 GraphQL 时超出了最大调用堆栈大小
Maximum call stack size exceeded on GraphQL Upload with NestJS
我在使用 ReadStream 函数通过 GraphQL Upload 上传文件时遇到错误:
error: 17:10:32.466+02:00 [ExceptionsHandler] Maximum call stack size exceeded
error: 17:10:32.467+02:00 [graphql] Maximum call stack size exceeded RangeError: Maximum call stack size exceeded
at ReadStream.open (/Users/xxxx/Documents/Xxxx/xxxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:80:7)
at _openReadFs (internal/fs/streams.js:117:12)
at ReadStream.<anonymous> (internal/fs/streams.js:110:3)
at ReadStream.deprecated [as open] (internal/util.js:96:15)
at ReadStream.open (/Users/xxxx/Documents/Xxxxx/xxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)
at _openReadFs (internal/fs/streams.js:117:12)
at ReadStream.<anonymous> (internal/fs/streams.js:110:3)
at ReadStream.deprecated [as open] (internal/util.js:96:15)
at ReadStream.open (/Users/xxxx/Documents/Xxxxx/xxxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)
at _openReadFs (internal/fs/streams.js:117:12) {"stack":"RangeError: Maximum call stack size exceeded\n at ReadStream.open (/Users/xxxx/Documents/Xxxxxx/xxxxx/xxxx-api/node_modules/fs-capacitor/lib/index.js:80:7)\n at _openReadFs (internal/fs/streams.js:117:12)\n at ReadStream.<anonymous> (internal/fs/streams.js:110:3)\n at ReadStream.deprecated [as open] (internal/util.js:96:15)\n at ReadStream.open (/Users/xxxxx/Documents/Xxxxx/xxxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)\n at _openReadFs (internal/fs/streams.js:117:12)\n at ReadStream.<anonymous> (internal/fs/streams.js:110:3)\n at ReadStream.deprecated [as open] (internal/util.js:96:15)\n at ReadStream.open (/Users/xxxx/Documents/Xxxxxx/xxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)\n at _openReadFs (internal/fs/streams.js:117:12)"}
(node:44569) [DEP0135] DeprecationWarning: ReadStream.prototype.open() is deprecated
(Use `node --trace-deprecation ...` to show where the warning was created)
这是我用来上传文件的函数:
public async cleanUpload(upload: GraphqlUpload, oldName?: string) {
let uploadResponse: FileInfo;
try {
if (oldName) {
this.safeRemove(oldName);
}
uploadResponse = await this.uploadFile(
{
fileName: upload.filename,
stream: upload.createReadStream(),
mimetype: upload.mimetype,
},
{ isPublic: true, filter: imageFilterFunction },
);
return uploadResponse;
} catch (e) {
this.logger.error('unable to upload', e);
if (uploadResponse) {
this.safeRemove(uploadResponse.fileName);
}
throw e;
}
}
解决方案是将节点版本从14.17降级到12.18。
要继续使用 Node 14.17,您可以禁用 Apollo 的内部上传并使用 graphql-upload
请参阅 this comment,其中概述了此处引用的方法。
For any future readers, here is how to fix the issue once and for all.
The problem is that @nestjs/graphql's dependency, apollo-server-core, depends on an old version of graphql-upload (v8.0) which has conflicts with newer versions of Node.js and various packages. Apollo Server v2.21.0 seems to have fixed this but @nestjs/graphql is still on v2.16.1. Furthermore, Apollo Server v3 will be removing the built-in graphql-upload.
The solution suggested in this comment is to disable Apollo Server's built-in handling of uploads and use your own. This can be done in 3 simple steps:
1. package.json
Remove the fs-capacitor and graphql-upload entries from the resolutions section if you added them, and install the latest version of graphql-upload (v11.0.0 at this time) package as a dependency.
2. src/app.module.ts
Disable Apollo Server's built-in upload handling and add the graphqlUploadExpress middleware to your application.
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")
}
}
3. src/blog/post.resolver.ts (example resolver)
Remove the GraphQLUpload import from apollo-server-core and import from graphql-upload instead
// import { GraphQLUpload } from "apollo-server-core" <-- remove this
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 */)
}
我在使用 ReadStream 函数通过 GraphQL Upload 上传文件时遇到错误:
error: 17:10:32.466+02:00 [ExceptionsHandler] Maximum call stack size exceeded
error: 17:10:32.467+02:00 [graphql] Maximum call stack size exceeded RangeError: Maximum call stack size exceeded
at ReadStream.open (/Users/xxxx/Documents/Xxxx/xxxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:80:7)
at _openReadFs (internal/fs/streams.js:117:12)
at ReadStream.<anonymous> (internal/fs/streams.js:110:3)
at ReadStream.deprecated [as open] (internal/util.js:96:15)
at ReadStream.open (/Users/xxxx/Documents/Xxxxx/xxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)
at _openReadFs (internal/fs/streams.js:117:12)
at ReadStream.<anonymous> (internal/fs/streams.js:110:3)
at ReadStream.deprecated [as open] (internal/util.js:96:15)
at ReadStream.open (/Users/xxxx/Documents/Xxxxx/xxxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)
at _openReadFs (internal/fs/streams.js:117:12) {"stack":"RangeError: Maximum call stack size exceeded\n at ReadStream.open (/Users/xxxx/Documents/Xxxxxx/xxxxx/xxxx-api/node_modules/fs-capacitor/lib/index.js:80:7)\n at _openReadFs (internal/fs/streams.js:117:12)\n at ReadStream.<anonymous> (internal/fs/streams.js:110:3)\n at ReadStream.deprecated [as open] (internal/util.js:96:15)\n at ReadStream.open (/Users/xxxxx/Documents/Xxxxx/xxxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)\n at _openReadFs (internal/fs/streams.js:117:12)\n at ReadStream.<anonymous> (internal/fs/streams.js:110:3)\n at ReadStream.deprecated [as open] (internal/util.js:96:15)\n at ReadStream.open (/Users/xxxx/Documents/Xxxxxx/xxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)\n at _openReadFs (internal/fs/streams.js:117:12)"}
(node:44569) [DEP0135] DeprecationWarning: ReadStream.prototype.open() is deprecated
(Use `node --trace-deprecation ...` to show where the warning was created)
这是我用来上传文件的函数:
public async cleanUpload(upload: GraphqlUpload, oldName?: string) {
let uploadResponse: FileInfo;
try {
if (oldName) {
this.safeRemove(oldName);
}
uploadResponse = await this.uploadFile(
{
fileName: upload.filename,
stream: upload.createReadStream(),
mimetype: upload.mimetype,
},
{ isPublic: true, filter: imageFilterFunction },
);
return uploadResponse;
} catch (e) {
this.logger.error('unable to upload', e);
if (uploadResponse) {
this.safeRemove(uploadResponse.fileName);
}
throw e;
}
}
解决方案是将节点版本从14.17降级到12.18。
要继续使用 Node 14.17,您可以禁用 Apollo 的内部上传并使用 graphql-upload
请参阅 this comment,其中概述了此处引用的方法。
For any future readers, here is how to fix the issue once and for all.
The problem is that @nestjs/graphql's dependency, apollo-server-core, depends on an old version of graphql-upload (v8.0) which has conflicts with newer versions of Node.js and various packages. Apollo Server v2.21.0 seems to have fixed this but @nestjs/graphql is still on v2.16.1. Furthermore, Apollo Server v3 will be removing the built-in graphql-upload.
The solution suggested in this comment is to disable Apollo Server's built-in handling of uploads and use your own. This can be done in 3 simple steps:
1. package.json
Remove the fs-capacitor and graphql-upload entries from the resolutions section if you added them, and install the latest version of graphql-upload (v11.0.0 at this time) package as a dependency.
2. src/app.module.ts
Disable Apollo Server's built-in upload handling and add the graphqlUploadExpress middleware to your application.
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") } }
3. src/blog/post.resolver.ts (example resolver)
Remove the GraphQLUpload import from apollo-server-core and import from graphql-upload instead
// import { GraphQLUpload } from "apollo-server-core" <-- remove this 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 */) }