GraphQL:在文件上传期间解决承诺时出错
Posted
技术标签:
【中文标题】GraphQL:在文件上传期间解决承诺时出错【英文标题】:GraphQL: Error when resolving promises during file upload 【发布时间】:2019-08-08 13:19:54 【问题描述】:所以我一直在处理 GraphQL 上传问题,在说明我的问题之前,先概述一下我正在使用的技术堆栈:
后端:Mongoose、Express、Apollo、GraphQL
前端:VueJS、Apollo、GraphQL
我正在使用Apollo Upload Client 将上传 文件从客户端发送到服务器端。由于我正在从客户端发送文件类型 scalar Upload 的列表,因此我收到了需要解决的承诺列表。在使用 Promise.all() 时,我收到以下错误(奇怪的是,我以前没有遇到过,我也不知道为什么)。如果我上传多个文件,第一个文件会丢失在某个地方,第二个文件会上传....但这并非总是如此。有时它不会发生。也许我没有正确解决或满足承诺。请注意,我还必须通过 Mongoose 将文件名保存在 MongoDB 中
BadRequestError: Request disconnected during file upload stream parsing.
at IncomingMessage.request.once (F:\repos\pushbox\node_modules\graphql-upload\lib\processRequest.js:245:35)
at Object.onceWrapper (events.js:285:13)
at IncomingMessage.emit (events.js:197:13)
at resOnFinish (_http_server.js:583:7)
at ServerResponse.emit (events.js:202:15)
at onFinish (_http_outgoing.js:683:10)
at processTicksAndRejections (internal/process/next_tick.js:74:9)
message: 'Request disconnected during file upload stream parsing.',
expose: true,
statusCode: 499,
status: 499
我有一个 html 文件输入标签,它接受多个文件,我使用的突变是:
async uploadFiles()
// Check if input tag is empty
if (this.files.length === 0)
this.uploadErrorAlert = true;
return;
// Mutation
this.isUploading = true;
await this.$apollo.mutate(
mutation: UPLOAD_FILES,
variables:
files: this.files,
id: this.selectedCard.id,
,
)
.then(() =>
// clear files from the input tag
this.files = '';
this.$refs.selectedFiles.value = '';
this.isUploading = false;
)
.catch((err) =>
console.error(err);
);
,
最后,服务器上的解析器是:
/**
* Uploads files sent on disk and saves
* the file names in the DB
*
* @param Object attachments - List of files for a card
*
* @return Boolean - true if upload is
* successful
*/
uploadFiles: async (_, attachments, controllers ) =>
Promise.all(attachments.files.map(async (file) =>
const createReadStream, filename = await file;
const stream = createReadStream();
/**
* We need unique names for every file being uploaded,
* so we use the ID generated by MongoDB and concat it
* to the filename sent by the user.
*
* Therefore we instantiate an attachment object to get an ID
*/
const attachment = await controllers.attachment.add( id: attachments.id, file: '' );
const newFileName = `$attachment.id_$filename`;
const path = `$process.env.UPLOAD_DIR/$newFileName`;
await controllers.attachment.update(
id: attachment.id,
file: newFileName,
);
console.log(`reached for $path`);
// Attempting to save file in server
return new Promise((resolve, reject) => stream
.pipe(createWriteStream(path))
.on('finish', () => resolve())
.on('error', (error) =>
console.log('dude?');
if (stream.truncated)
// Delete the truncated file
unlinkSync(path);
reject(error);
));
)).then(() =>
pubsub.publish(ATTACHMENTS_ADDED, attachmentsChanged: controllers.attachment.getAll() );
).catch((err) =>
console.log(err);
);
,
任何帮助将不胜感激!
【问题讨论】:
我认为您将文件发送到后端的方式可能存在问题。通常要上传文件并将其发送到后端,您必须使用 formData 对象。您必须遍历每个文件并将其作为属性附加。 const fd = new FormData(); this.files.forEach((file) => fd.append('files', file) 所以我只是尝试过,但我在后端什么也没收到。当我在没有 formData 的情况下发送它时,即只发送 this.files,然后我会收到文件。后端 formData 的控制台: id: '5c8b7b5959fa58d85041ba34', files: [ ]
后端 this.files 的控制台: id: '5c8b7b5959fa58d85041ba34', files: [ Promise [Object] , Promise [Object] , Promise [Object] ]
【参考方案1】:
好吧,我不知道我是怎么错过this issue 的,但是这就是解决方案!问题出在我正在使用的模块的 github 问题论坛上。
所以问题是通过在Promise.all()
函数之前使用await
来解决的。所以现在uploadFiles
解析器中的代码如下所示:
await Promise.all(attachments.files.map(async (file) =>
const createReadStream, filename = await file;
const stream = createReadStream();
/**
* We need unique names for every file being uploaded,
* so we use the ID generated by MongoDB and concat it
* to the filename sent by the user.
*
* Therefore we instantiate an attachment object to get an ID
*/
const attachment = await controllers.attachment.add( id: attachments.id, file: '' );
const newFileName = `$attachment.id_$filename`;
const path = `$process.env.UPLOAD_DIR/$newFileName`;
await controllers.attachment.update(
id: attachment.id,
file: newFileName,
);
console.log(`reached for $path`);
// Attempting to save file in server
return new Promise((resolve, reject) => stream
.pipe(createWriteStream(path))
.on('finish', () => resolve())
.on('error', (error) =>
console.log('dude?');
if (stream.truncated)
// Delete the truncated file
unlinkSync(path);
reject(error);
));
)).then(() =>
pubsub.publish(ATTACHMENTS_ADDED, attachmentsChanged: controllers.attachment.getAll() );
).catch((err) =>
console.log(err);
);
【讨论】:
以上是关于GraphQL:在文件上传期间解决承诺时出错的主要内容,如果未能解决你的问题,请参考以下文章
上传时提示:将文件复制到ftp服务器出错,请检查是不是有权限将文件放到该服务器上。
使用 graphql 文件编译 Typescript 时出错
使用 nexus-prisma graphql 处理文件上传
laravel 8 中的文件上传 - 在 null 上调用成员函数 getClientOriginalName() 时出错