37

Is there a way to send an array of images (or a single image) to node using axios?

The axios code I'm using(I'm using react js on the front end):

onFormSubmit(event){
    event.preventDefault();
    let payload = this.state;
    console.log("in onFormSubmit!!! with state: ", this.state, "and payload: ", payload);
    axios.post('/api/art', payload)
    .then(function(response){
    console.log('saved successfully')
  }); 

The research I've done suggests that perhaps there isn't a supported way to send image files to node using axios, but this seems strange to me. Is there a way?

Junayed
  • 91
  • 6
J. Bones
  • 459
  • 1
  • 5
  • 9

6 Answers6

37

Here is the way I got this to work properly. I had to make use of an object called FormData. I used the import:

import FormData from 'form-data'

Of course before this import I had to run the npm install for it:

npm install --save form-data

Once I did all that, here is how I used it within my action:

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

return (dispatch) => {
axios.post(URL, data, {
  headers: {
    'accept': 'application/json',
    'Accept-Language': 'en-US,en;q=0.8',
    'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
  }
})
  .then((response) => {
    //handle success
  }).catch((error) => {
    //handle error
  });
};}

The important pieces to note here are:

  1. I included some headers as a configuration object after the data object passed into the axios.post call. The content type you're including here is the key. You're submitting a multipart/form-data content type.
  2. Within that Content type header, I also added a boundary, which is derived from the data object you created earlier.
  3. The 'file' used here is just the file object I passed into my action. It's just the name I used for my object, you can use anything you want here.

Hope this helps, this cleared up all of the issues I had with trying to submit an image to a backend (in my case a rest service - through a post call).

Jota Renan
  • 266
  • 2
  • 4
  • 11
Cmontalvo80
  • 371
  • 3
  • 2
  • 15
    You shouldn't need to import FormData, unless you are trying to polyfill for IE<=9: https://caniuse.com/#feat=xhr2 – undefined Jun 20 '17 at 21:17
  • 1
    @undefined using FormData for uploading files instead of puting it in the body of a HTTP_MESSAGE is completely acceptable, because [most of the body-parser libraries cannot handle multipart requests bodies and parsing body with your own is not recommended at all!](https://stackoverflow.com/a/4526286/6791254) as well as this approach is the [only recommended one even with axios official documentation for posting files](https://github.com/axios/axios#form-data) – TechDogLover OR kiaNasirzadeh Oct 18 '20 at 04:51
  • according to [MDN web docs](https://developer.mozilla.org/en-US/docs/Web/API/File/fileName) file.fileName is Obsoleted and non-standard so you should use `file.name` instead of `file.fileName` – TechDogLover OR kiaNasirzadeh Oct 19 '20 at 08:10
  • 1
    @ashkannasirzadeh yes it is completely acceptable; I didn't suggest that it wasn't. Only that you don't need to import it from a package because it is native to every browser newer than IE 9. – undefined Oct 19 '20 at 16:42
  • 2
    i had a hard time using `$_FILES` variable of an api i'm writing in php, after inspecting the request which i'm sending to server, guess what! i was using `axios.post('file', {file: myFormData}}` instead of `axios.post('file', myFormData)`. this may save some hours for someone – TechDogLover OR kiaNasirzadeh Oct 20 '20 at 16:27
  • hey @undefined. I am having an issue with a block of code while trying to send file data from react frontend to sequelize backend. Would the solution above work minus the importing FormData. Here is my original question/issue https://stackoverflow.com/questions/70781569/react-axios-request-object-is-not-being-sent-to-the-backend-controller-properly?noredirect=1#comment125131225_70781569 – omar jandali Jan 20 '22 at 07:20
  • I had a hard time as well, but after several hours, i learned that middleware is needed to parse the images. Hopefully this helps others. i used multer. – Martin Watson Jan 10 '23 at 04:10
18

Yes you will have to set the content type in your axios request:

axios.put(url, imageFile, {
  headers: {
    'Content-Type': imageFile.type
  }
});

where imageFile is an HTML5 file object which should be an image in your case.

Yangshun Tay
  • 49,270
  • 33
  • 114
  • 141
13

Here is how I implemented it:

onFormSubmit(event){
    var form = new FormData();
    files.forEach(file => {
        form.append(file.name, file);
    });
    form.append('foo', 'bar');
    axios.post('/api/art', form)    
}); 

On node js server make sure to use some middle-ware which handles multipart requests. I used multer.

Here are my results on the endpoint:

req.body - { foo: 'bar' }
req.files - { 
    'r1.jpg': { 
      fieldname: 'r1.jpg',
      originalname: 'r1.jpg',
      name: 'e2f4b9874fd7d6115b9f7440b9ead3a0.jpg',
      encoding: '7bit',
      mimetype: 'image/jpeg',
      path: '/tmp/e2f4b9874fd7d6115b9f7440b9ead3a0.jpg',
      extension: 'jpg',
      size: 45641,
      truncated: false,
      buffer: null 
    }, ...
}
Volodymyr Synytskyi
  • 3,885
  • 1
  • 15
  • 19
8

With HTML5 you can use FormData() to construct a set of key/value pairs representing form fields and their values you want to send. In most cases, as in a user submitting a form, the method to use is FormData.set() which can be manipulated in its 2 forms:

There are two versions of this method: a two and a three parameter version:

formData.set(name, value);

formData.set(name, value, filename);

Once you construct your data object, do not forget to specify the multipart content type header for your HTTP POST request so that you can send the file to your server.

Below is a summary of what I said:

onFormSubmit(event){
    let formData = new FormData(); // instantiate it
    // suppose you have your file ready
    formData.set('file', yourFile);
    // add some data you collected from the input fields
    formData.set('data1', dataInputField1); // suppose you got dataInputField1 from your HTML5 form input
    axios.post('/api/art', formData, {
      headers: {
       'content-type': 'multipart/form-data' // do not forget this 
      }})
  }
Billal Begueradj
  • 20,717
  • 43
  • 112
  • 130
2

I would say instead of doing this manually you can use a library called react-dropzone for this . So basically what you need to do is :-

import React,{Component} from 'react';
import Dropzone from 'react-dropzone';
import request from 'superagent';

class DropZone extends Component{

  onDrop(files){
    var file = new FormData();
    file.append('name',files[0])
    var req=request
              .post('http://localhost:8000/api/v0/image/')
              .send(file);
    req.end(function(err,response){
        console.log("upload done!!!!!");
    });
  }

  render(){
    return(
      <div>
        <Dropzone onDrop={this.onDrop}>
          <div>Try dropping some files here, or click to select files to upload.</div>
        </Dropzone>
      </div>
          );
  }
}

You can check here for git repo. I have implemented this in django but i dont think backend should be a problem, you can use node

Harkirat Saluja
  • 7,768
  • 5
  • 47
  • 73
  • hey thanks for the suggestion. I implimented this but I'm still getting the same bug. in node js, when I console log the body of the request, it only logs the image preview of a file, and not the file itself. – J. Bones Sep 23 '16 at 21:12
  • try to log req.files and make sure to use some middle-ware which handles multipart requests (multer) – Volodymyr Synytskyi Jul 06 '17 at 10:19
2

All we need to upload an image to a backend is header multipart/form-data, image details (uri, name, type) and form-data.

import FormData from 'form-data'

const config = {
   headers: {

    'Content-Type': 'multipart/form-data'

   }
}


const data = new FormData();

data.append('image', {
    uri: image.uri,
    name: image.uri.split('/').pop(), //split the uri at / and get the last element of the resulting array which actually is the name with the image extention (e.g, abc.jpg)
   type: image.type // type needs to be modified. keep reading
})

Now here is an important point. Many times the type from the image details is not the complete type (like, it returns only 'image' but the actual type is 'image/jpeg') and this results in network error.

Now to get the accurate type, we can use some third party package to do so.

E.g, we can use mime.

import mime from 'mime'

type: mime.getType(image.uri)

Then at last, we just need to send the request.

axios.post('url', data, config)
.then(...)
.catch(...)

Not only images, but we can also upload videos with the exact same procedure.

Irfan wani
  • 4,084
  • 2
  • 19
  • 34