jaydenseric / graphql-upload

Middleware and a scalar Upload to add support for GraphQL multipart requests (file uploads via queries and mutations) to various Node.js GraphQL servers.
https://npm.im/graphql-upload
MIT License
1.43k stars 132 forks source link

FileStreamDisconnectUploadError on v5.0.0 and v6.0.0.alpha #100

Closed dohomi closed 6 years ago

dohomi commented 6 years ago

Hello,

I was reading through several issues and the merged PR #81 but still I resolve my current task: I need multiple file attachment to send the files with the mailgun-js api. I tried

const currentAttachments = await Promise.all(attachments) // fails

// another approach for loop
const currentAttachments = []
for(const attachment of attachments){
 const t = await attachment
 currentAttachments.push(t)
}

As soon I choose more than 2 attachments I receive FileStreamDisconnectUploadError on either of the tryouts above. The major issue is that I need to prepare the readable stream to match the api https://github.com/bojand/mailgun-js#attachments. But I never really get to the point to map the attachments because after the second file I receive FileStreamDisconnectUploadError. I was reading through some closed issue and upgraded to v6.0.0.alpha but that didn't solved the issue. How can I handle the process of all attachments to send them all via an external API?

jaydenseric commented 6 years ago

Most likely you are not awaiting all the streams to be complete before the resolver returns. Once all the resolvers return, the GraphQL middleware sends a response which disconnects the request if it is still streaming up.

To await the streams, you need to promisify the right events like this example.

dohomi commented 6 years ago

@jaydenseric I used that example to built my function. Do I need to store the files in a temp folder to process them later on? I only want to "pass" the readable stream to my mailgun api and sent out the email. I thought I could simply return the stream as it is used here: https://github.com/jaydenseric/apollo-upload-examples/blob/master/api/resolvers.mjs#L43

const processUpload = async upload => {
  return await upload
}

I dropped some breakboints there but after the 2nd or third attachments it stops processing

dohomi commented 6 years ago

this is my current tryout:

        const tempFiles = await Promise.all(attachments.map(async f => {
          return await f
        }))

I can set up a breakpoint inside of the mapping function and see that it hangs after the second loop

jaydenseric commented 6 years ago

You can pass the readable stream into whatever API, but you need to be awaiting the completion of the streams. The way to do that is to have even listers on each stream.

What you are doing currently is just awaiting the Upload scalar promises, but not the streams that they resolve. By resolving before mailgun has received the whole stream, you are sending a premature response back to the client wich disconnects the current stream.

jaydenseric commented 6 years ago

It's pretty tricky, feel free to chat about it on the #uploads channel of the Apollo Slack if you get stuck ☺️

dohomi commented 6 years ago

@jaydenseric thanks very helpful information! I will switch over to that channel. Currently I don't see it how can I enable it?

jaydenseric commented 6 years ago

In the left Slack sidebar, you can can click on the "Channels" heading to find it.