Problems with XHRUpload

Hey guys!
I am trying to use uppy in a node(express) project and I am having a bad time trying to use it.

Here is my Companion config:

const optionsCompanion = {
  providerOptions: {
    google: {
      key: process.env.GOOGLE_CLIENT_ID,
      secret: process.env.GOOGLE_SECRET_KEY_CLIENT,
    },
    dropbox: {
      key: process.env.DROPBOX_KEY,
      secret: process.env.DROPBOX_SECRET_KEY,
    },
  },
  server: {
    host: "localhost:3020",
    protocol: "http",
  },
  filePath: "./public/upload",
  secret: "xxxx", //same session secret
};

app.use(companion.app(optionsCompanion));
var serverUppy = app.listen(3020);
companion.socket(serverUppy, optionsCompanion);

And here is my uppy client config:

var uppy = Uppy.Core({
  restrictions: {
    maxFileSize: 52428800,
    minNumberOfFiles: 1,
    allowedFileTypes: arrayExtensoes(),
  },
  meta: {
    TamanhoMaxPorDocumento: document.querySelector("#TamanhoMaxPorDocumento")
      .value,
    MaxDocsPorEnvelope: document.querySelector("#MaxDocsPorEnvelope").value,
    latitude: document.querySelector('[name="latitude"]').value,
    longitude: document.querySelector('[name="longitude"]').value,
    browser: document.querySelector('[name="browser"]').value,
    so: document.querySelector('[name="so"]').value,
    ip: document.querySelector('[name="ip"]').value,
    idpasta: document.querySelector('[name="idpasta"]').value,
    repositorio: document.querySelector('[name="repositorio"]').value,
    gerarTags: document.querySelector('[name="gerarTags"]').value,
    descricao: document.querySelector('[name="descricao"]').value,
    nomerepositorio: document.querySelector('[name="nomerepositorio"]').value,
  },
});

uppy
  .use(Uppy.Dashboard, {
    target: "body",
    trigger: "#add-documents",
    closeModalOnClickOutside: true,
    closeAfterFinish: true,
    showProgressDetails: true,
    height: 300,
    locale: Uppy.locales.pt_BR,
    proudlyDisplayPoweredByUppy: false,
    showRemoveButtonAfterComplete: false,
    browserBackButtonClose: true,
  })
  .use(Uppy.GoogleDrive, {
    target: Uppy.Dashboard,
    companionUrl: "localhost:3020",
    companionHeaders: {
      "Acess-Control-Allow-Origin": " <em>",
      "Access-Control-Allow-Methods": "OPTIONS, GET, POST, PATCH, PUT",
      "Access-Control-Allow-Headers":
        "Origin, X-Requested-With, Content-Type, Accept, Authorization, Extra-Data",
    },
  })
  .use(Uppy.Dropbox, {
    target: Uppy.Dashboard,
    companionUrl: "localhost:3020",
    companionHeaders: {
      "Acess-Control-Allow-Origin": "</em> ",
      "Access-Control-Allow-Methods": "OPTIONS, GET, POST, PATCH, PUT",
      "Access-Control-Allow-Headers":
        "Origin, X-Requested-With, Content-Type, Accept, Authorization, Extra-Data",
    },
  })
  .use(Uppy.XHRUpload, {
    method: "post",
    endpoint: "localhost:3000/envelopes",
    formData: true,
    fieldName: "files",
    headers: {
      "Acess-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "OPTIONS, GET, POST, PATCH, PUT",
      "Access-Control-Allow-Headers":
        "Origin, X-Requested-With, Content-Type, Accept, Authorization, Extra-Data",
    },
  });

I am trying to send some input hidden (you can see those inputs in meta) with XHRUpload, but I am having a lot of troubles. I can’t find the inputs values in my controller when debugging my request variable.
The other problems was solved with this line:

app.use(bodyParser.json({ type: ['application/json', 'text/plain'] }))
app.use(cors())
app.options('*', cors())

But I am still having some troubles with cors policy.
The upload seems to work as you can see here in companion log:

companion: 2020-08-05T19:30:12.418Z [debug] 009bbf7a uploader.upload.progress 1102331 1102331 100.00%

But after i got this error: companion: 2020-08-05T19:30:12.428Z [error] upload.multipart.error upload failed with status: 400

And in console i got these errors:

Access to fetch at localhost/drive/list/root from origin localhost:3000 has been blocked by CORS policy: Method OPTIONS is not allowed by Access-Control-Allow-Methods in preflight response.
RequestClient.js:1 OPTIONS localhost:3020/drive/list/root net::ERR_FAILED.
Access to fetch at localhost:3020/drive/get/1zz-IOdBjEZyvX2_Z_rd6sx1YPeBZ7f-a' from origin localhost:3000 has been blocked by CORS policy: Method OPTIONS is not allowed by Access-Control-Allow-Methods in preflight response.
OPTIONS localhost:3020/drive/get/1zz-IOdBjEZyvX2_Z_rd6sx1YPeBZ7f-a net::ERR_FAILED.
[Uppy] [16:18:37] Failed to upload Assinatura Tomás.jpg Bad Request.

I was trying using Form too, but when the input hidden with file data was attached in my the data value of file was a blank object, and when i submit the form i was not able to save the file in database.

I also would like to know how to send an array of files to my endpoint, because my users should be able to select multiples files. At the moment when i select multiple files only the last one is sent to my controller.

I would love any help.
Thanks

Tomás Loureiro Gomes

I suspect that this issue could be coming from you using the app.options('*', cors()) call.

This quote comes from the cors package README

NOTE: When using this middleware as an application level middleware (for example, app.use(cors()) ), pre-flight requests are already handled for all routes.

That means that you don’t need this part of the code

app.options('*', cors())

If that doesn’t work, let me know and I’ll look a little more into it

- Andrew

Upon further investigation, you asked this question on the GitHub issue tracker (here) and it was resolved

Yes it was resolved. Thanks!
I would like to ask one more thing.
How can i use uppy to upload multiples files at once using drive and dropbox?
I read in the docs that it isn’t possible. I am trying to make a middleware to save in an array all the docs and after send to other route. But i am having a bad time trying to figure it out.
Also, all the time that i’ve tryed to access the data (blob object) it is always blank.
I tryed to use with Form to append all the docs that i need and then make a submit, but i am not able to see anything in data (always empty { } ).
How can i use Form or XHRUpload and then submit to my route all docs at once?
Do you have any ideas?

Right, you cannot send multiple files in one request with Companion. An option would be to store all the files temporarily on your backend, and then have a separate route that can collect these files and send them to the final destination. Then you can send a request to that separate route from the client.

uppy.on('complete', (result) => {
  fetch('/path/to/endpoint/that/collects/all/the/files', {
    method: 'post',
    // you should decide what the `body` should be here, this is just an example
    body: JSON.stringify({
      // Assuming that your initial endpoint that receives the files responds with a JSON
      // object with a `temporaryId` key:
      files: result.successful.map((file) => file.response.body.temporaryId)
    })
  })
})

For the Blob problem, note that blob data can only be read asynchronously using a FileReader. For files added from remote sources like Companion, we do not have a Blob object, because those files are never downloaded to the client.

Hey I have figure it out how to do it.
I am using an global array and 2 routes. The first route I’m using to push files into the array, when i push all the files I call the next route. Finally I send the files to my database in the second route. Everything works but Uppy not. Progress is showing wrong values and I don’t know what to do to avoid this. In my head all the progress should finish when the first route got to an end. In the picture below you can see the problem:

All the files are uploaded to my database and its is fine, but uppy get lost about progress and its state.

Hey, here is my code in backend, pls can you point me where i’am wrong?

router.post("/", upload.array('files'), (req, res, next) => { 
  listaArquivosGlobal.push({
     nomeArquivo: req.files[0].originalname,
     mimeType: req.files[0].mimetype,
     conteudo: req.files[0].buffer.toString('base64')
  })
  //it means all files has been push to array
  if(listaArquivosGlobal.length == +req.body.qtdArquivos) next()
})

And then the next route:

router.post("/", async (req, res, next) => {
   try {
       if(!req.user) {
           req.user = JSON.parse(req.body.user)
           res.locals.args = JSON.parse(req.body.locals)
       }

       const envelope = {
           descricao: req.body.descricao,
           Repositorio: { id: req.body.repositorio || req.body.idrepositorio, nome: req.body.nomerepositorio, Usuario: { id: req.user.id } },
           Pasta: { id: req.body.idpasta },
           Usuario: { id: req.user.id },
           listaDocumentos: { Documento: [] }
       }

       //here i push the files
       listaArquivosGlobal.forEach(f => {
           envelope.listaDocumentos.Documento.push({
               nomeArquivo: f.nomeArquivo,
               mimeType: f.mimeType,
               conteudo: f.conteudo
           })
       })
       
       //send files to my database
       const retorno = await ws.callMethod(
           'inserirEnvelope',
           { Envelope: envelope, gerarTags: req.body.gerarTags || 'N' },
           res.locals.args
       )
   
       req.flash("success_msg", retorno.mensagem)
       req.flash('envelopeAuth', 'S')
       res.status(200).send({msg: retorno.mensagem, id: retorno.data.idEnvelope})
       listaArquivosGlobal = []
    } catch (error) {
        res.status(400).send(error.message || error)
        listaArquivosGlobal = []
    }
})

In this code, everything works fine. But in Uppy all the progress are wrong and it seems it keeps waiting for a response. I’m new to express and I’m not sure how middlewares work. Can you pls help me?