jaydenseric / graphql-upload

Middleware and an Upload scalar 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 131 forks source link

FileList is supported but not an array/object with multiple files. #291

Closed mateusmx closed 2 years ago

mateusmx commented 2 years ago

Hi. I'm facing with an issue where I'm able to get multiple files when making a simple <input type="file" multiple> but I'm facing issues when trying to integrate Dropzone UI which is giving me back an Array of files.

I'm using type-graphql with apollo-server-exprees. Here is my resolver:

@Mutation(() => Boolean)
  @UseMiddleware(isAdmin)
  async fileUpload(
    @Arg('files', () => [GraphQLUpload])
    filesArray: Array<FileUpload>,
  ) {
    filesArray.map(async (item) => {
      const { createReadStream, filename } = await item;

      const fileRenamer = (filename: string): string => {
        const queHoraEs = Date.now();
        const regex = /[\s_-]/gi;
        const fileTemp = filename.replace(regex, '.');
        let arrTemp = [fileTemp.split('.')];
        return `${arrTemp[0]
          .slice(0, arrTemp[0].length - 1)
          .join('_')}${queHoraEs}.${arrTemp[0].pop()}`;
      };

      const stream = createReadStream();
      const assetUniqName = fileRenamer(filename);
      const pathName = path.join(__dirname, `../uploads/${assetUniqName}`);
      await stream.pipe(fs.createWriteStream(pathName));
      const urlForArray = `${process.env.SERVER_URL}/${assetUniqName}`;
      console.log(urlForArray);
    });
    return true;
  }

And here is a picture of what I send (In the top the 'failing' in the bottom the 'success'. In the top I manually made an object with the array of files I get from dropzone in the bottom is what I get from the regular input file)

Captura de Pantalla 2022-01-16 a la(s) 15 52 38

I find out that FileList is a read-only and we can't create one from 0. So I'm stuck here. Also Attaching a picture of the answer I get from the BE.

image

And this of course is only happening when trying to push from the Object I created or from the Array I got from Dropzone (tried both ways).

Any help is very much appreciated.

jaydenseric commented 2 years ago

Always the first thing to do when debugging GraphQL file upload problems is to check if the client is sending valid GraphQL multipart requests to the server. This way, you can focus on which side is faulty, instead of wondering.

To do this, make the request with your browser's network inspector open. You should be able to inspect the body of the multipart request and make sure it follows the spec. Binary file data is hidden in the inspector for file fields, that's ok.

mateusmx commented 2 years ago

Thanks @jaydenseric for such a quick response.

This is what I get from the one that fails: image

This is what I get from the one that pass through image

Pretty much the same, but what I can see is that for some reason they are getting 'mapped' differently 🤔 Any thoughts on that ?

jaydenseric commented 2 years ago

For the one that fails, you say in the GraphQL query that files is an array, but if you look at files within the GraphQL operation variables JSON, it's an object. That's your problem, it should be an array. This is a problem with how your client is preparing the variables, not a problem on the server or an issue with graphql-upload.

mateusmx commented 2 years ago

Hi @jaydenseric I did tried both methods (as an Array and as an Object) and keep having the same failure. Attaching pictures of what I get when I send an array: image

image

image

jaydenseric commented 2 years ago

@mateusmx consider carefully this part of your query: $files: [Upload!]!. What you are doing in variables JSON is not that. You are doing an array that incorrectly contains objects that have the Upload scalar nested a layer deeper.

mateusmx commented 2 years ago

Hi @jaydenseric. I was able to make it work. Apparently when I was sending the array from the Dropzone integration, it was being 'rejected' with the errors shared before. So what was needed to do is to iterate that array from Dropzone and create a new one with only the file. I'll try to get sometime to write an article and share it so more persons can solve this.

Thank you so much for your help.