superagent: missing boundary in Content-Type for multipart posts

I’m attempting to use superagent to upload files to Cloudinary. I have the following code:

    const settings = {
      CLOUDINARY_API_KEY: {REDACTED},
      CLOUDINARY_ACCOUNT_NAME: {REDACTED},
      CLOUDINARY_UPLOAD_PRESET: {REDACTED}
    };

    const req = request.post(`https://api.cloudinary.com/v1_1/${settings.CLOUDINARY_ACCOUNT_NAME}/image/upload`);
    files.forEach((file) => {
      req.attach(file.name, file);
    });
    req.set('Accept', 'application/json');
    req.set('X-Requested-With', 'XMLHttpRequest');
    req.attach('api_key', settings.CLOUDINARY_API_KEY);
    req.attach('timestamp', Date.now() / 1000);
    req.attach('upload_preset', settings.CLOUDINARY_UPLOAD_PRESET);
    req.type('multipart/form-data');
    req.end((err, res) => {
      if (err) {
        console.log('error', err);
      } else {
        console.log('success', res);
      }
    });

Running on localhost, I’m getting the error from their API:

XMLHttpRequest cannot load https://api.cloudinary.com/v1_1/dmzafaonc/image/upload. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3030' is therefore not allowed access. The response had HTTP status code 502.

But I believe this is a red herring, since I’m able to upload successfully with the Dropzone library, loaded from a local dropzone.js file. It also gives an error response if I comment out the file attachments - it correctly tells me that a parameter is missing.

This led me to look into the request in Chrome Inspector:

Request Headers:

Accept:application/json
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Content-Length:31756
Content-Type:multipart/form-data
DNT:1
Host:api.cloudinary.com
Origin:http://localhost:3030
Pragma:no-cache
Referer:http://localhost:3030/drop
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.99 Safari/537.36
X-Requested-With:XMLHttpRequest

Request Payload:

------WebKitFormBoundary9uDkePsFayAfLf8a
Content-Disposition: form-data; name="birthdaybeer_400.jpg"; filename="undefined"
Content-Type: image/jpeg


------WebKitFormBoundary9uDkePsFayAfLf8a
Content-Disposition: form-data; name="api_key"


------WebKitFormBoundary9uDkePsFayAfLf8a
Content-Disposition: form-data; name="timestamp"


------WebKitFormBoundary9uDkePsFayAfLf8a
Content-Disposition: form-data; name="upload_preset"


------WebKitFormBoundary9uDkePsFayAfLf8a--

Note the Content-Type field, which is lacking the boundary. That’s the only difference I can see from the corresponding dropzone.js request and the superagent one: Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryzg196Oz74o5Y3jvp

I cannot set the Content-Type manually with the boundary because it is generated by superagent.

Any ideas?

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 9
  • Comments: 21 (2 by maintainers)

Commits related to this issue

Most upvoted comments

I found that uploading files without specifying the Content-Type worked for me. In the request, I saw that Superagent was able to determine that the Content-Type was multipart/form-data and appended the correct boundary. It worked successfully.

As a workaround, I got this to work, with the proper Content-Type set, using FormData:

    const req = request.post(`https://api.cloudinary.com/v1_1/${settings.CLOUDINARY_ACCOUNT_NAME}/image/upload`);

    const data = new FormData();
    files.forEach((file) => {
      data.append('file', file);
    });
    data.append('api_key', settings.CLOUDINARY_API_KEY);
    data.append('timestamp', Date.now() / 1000);
    data.append('upload_preset', settings.CLOUDINARY_UPLOAD_PRESET);

    req.send(data);
    req.end((err, res) => {
      if (err) {
        console.log('error', err);
      } else {
        console.log('success', res);
      }
    });

req.set(‘enctype’, ‘multipart/form-data’)

I think the solution is to never set content-type manually for multipart.

@meenalt Why can’t you use FormData in React? I ended up getting this working using the following:

let data = new FormData();
data.append('file', file, file.name);

superagent
    .post('url')
    .send(data)
    .end((ex, result) => {

    });

I tried setting content type to undefined and it works

.set(‘Content-Type’, undefined)

You should probably use field for non-file data. Like req.field('name', 'John Doe')