React.js, how to send a multipart/form-data to server
Asked Answered
B

9

50

We want to send an image file as multipart/form to the backend, we try to use html form to get file and send the file as formData, here are the codes

export default class Task extends React.Component {

  uploadAction() {
    var data = new FormData();
    var imagedata = document.querySelector('input[type="file"]').files[0];
    data.append("data", imagedata);

    fetch("http://localhost:8910/taskCreationController/createStoryTask", {
      mode: 'no-cors',
      method: "POST",
      headers: {
        "Content-Type": "multipart/form-data"
        "Accept": "application/json",
        "type": "formData"
      },
      body: data
    }).then(function (res) {
      if (res.ok) {
        alert("Perfect! ");
      } else if (res.status == 401) {
        alert("Oops! ");
      }
    }, function (e) {
      alert("Error submitting form!");
    });
  }

  render() {
    return (
        <form encType="multipart/form-data" action="">
          <input type="file" name="fileName" defaultValue="fileName"></input>
          <input type="button" value="upload" onClick={this.uploadAction.bind(this)}></input>
        </form>
    )
  }
}

The error in backend is "nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found".

After reading this, we tried to set boundary to headers in fetch:

fetch("http://localhost:8910/taskCreationController/createStoryTask", {
      mode: 'no-cors',
      method: "POST",
      headers: {
        "Content-Type": "multipart/form-data; boundary=AaB03x" +
        "--AaB03x" +
        "Content-Disposition: file" +
        "Content-Type: png" +
        "Content-Transfer-Encoding: binary" +
        "...data... " +
        "--AaB03x--",
        "Accept": "application/json",
        "type": "formData"
      },
      body: data
    }).then(function (res) {
      if (res.ok) {
        alert("Perfect! ");
      } else if (res.status == 401) {
        alert("Oops! ");
      }
    }, function (e) {
      alert("Error submitting form!");
    });
  }

This time, the error in backend is: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

Do we add the multipart boundary right? Where should it be? Maybe we are wrong at first because we don't get the multipart/form-data. How can we get it correctly?

Blackguard answered 12/1, 2017 at 10:30 Comment(0)
B
27

We just try to remove our headers and it works!

fetch("http://localhost:8910/taskCreationController/createStoryTask", {
      mode: 'no-cors',
      method: "POST",
      body: data
    }).then(function (res) {
      if (res.ok) {
        alert("Perfect! ");
      } else if (res.status == 401) {
        alert("Oops! ");
      }
    }, function (e) {
      alert("Error submitting form!");
    });
Blackguard answered 13/1, 2017 at 12:6 Comment(2)
how do you process this data in the backend? assuming php @PeterJiangOstrogoth
We did not use Php, but JavaBlackguard
H
29

Here is my solution for image upload with preview through axios.

import React, { Component } from 'react';
import axios from "axios";

React Component Class:

class FileUpload extends Component {

    // API Endpoints
    custom_file_upload_url = `YOUR_API_ENDPOINT_SHOULD_GOES_HERE`;


    constructor(props) {
        super(props);
        this.state = {
            image_file: null,
            image_preview: '',
        }
    }

    // Image Preview Handler
    handleImagePreview = (e) => {
        let image_as_base64 = URL.createObjectURL(e.target.files[0])
        let image_as_files = e.target.files[0];

        this.setState({
            image_preview: image_as_base64,
            image_file: image_as_files,
        })
    }

    // Image/File Submit Handler
    handleSubmitFile = () => {

        if (this.state.image_file !== null){

            let formData = new FormData();
            formData.append('customFile', this.state.image_file);
            // the image field name should be similar to your api endpoint field name
            // in my case here the field name is customFile

            axios.post(
                this.custom_file_upload_url,
                formData,
                {
                    headers: {
                        "Authorization": "YOUR_API_AUTHORIZATION_KEY_SHOULD_GOES_HERE_IF_HAVE",
                        "Content-type": "multipart/form-data",
                    },                    
                }
            )
            .then(res => {
                console.log(`Success` + res.data);
            })
            .catch(err => {
                console.log(err);
            })
        }
    }


    // render from here
    render() { 
        return (
            <div>
                {/* image preview */}
                <img src={this.state.image_preview} alt="image preview"/>

                {/* image input field */}
                <input
                    type="file"
                    onChange={this.handleImagePreview}
                />
                <label>Upload file</label>
                <input type="submit" onClick={this.handleSubmitFile} value="Submit"/>
            </div>
        );
    }
}

export default FileUpload;
Hoi answered 2/12, 2019 at 7:51 Comment(6)
Straight to the point! And thanks for the base64 addition.Hoffer
Glad to know you like it @HofferHoi
What is base64 do here? when i console.log i got the following thing that i dont understand blob:http://localhost:3000/7454bf5a-e458-4c30-bbcc-ac0674ade821Norvol
@RobyCigar Base64 is used to transmit the data without loss or modification of the contents themselves. It is a way to encode binary data into an ASCII character set.Hoi
to prevent broken image (by default view) until one is selected section preview image should be in conditional : {this.state.image_preview ? (<img src={this.state.image_preview} alt="image preview" />) : null} so not to display <img /> tag with no src attributeCerulean
Good to know that it worked for you @IbrahimHossainHoi
B
27

We just try to remove our headers and it works!

fetch("http://localhost:8910/taskCreationController/createStoryTask", {
      mode: 'no-cors',
      method: "POST",
      body: data
    }).then(function (res) {
      if (res.ok) {
        alert("Perfect! ");
      } else if (res.status == 401) {
        alert("Oops! ");
      }
    }, function (e) {
      alert("Error submitting form!");
    });
Blackguard answered 13/1, 2017 at 12:6 Comment(2)
how do you process this data in the backend? assuming php @PeterJiangOstrogoth
We did not use Php, but JavaBlackguard
R
10

The file is also available in the event:

e.target.files[0]

(eliminates the need for document.querySelector('input[type="file"]').files[0];)

uploadAction(e) {
  const data = new FormData();
  const imagedata = e.target.files[0];
  data.append('inputname', imagedata);
  ...

Note: Use console.log(data.get('inputname')) for debugging, console.log(data) will not display the appended data.

Rosenzweig answered 18/7, 2018 at 0:54 Comment(1)
Thanks for console.log(data.get('inputname')). But why does it return empty for console.log(data)? For multiple files I am using console.log(data.getAll('inputname'))Boatman
V
7

https://muffinman.io/uploading-files-using-fetch-multipart-form-data/ worked best for me. Its using formData.

import React from "react";
import logo from "./logo.svg";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import Button from "react-bootstrap/Button";

const ReactDOM = require("react-dom");


export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.test = this.test.bind(this);
    this.state = {
      fileUploadOngoing: false
    };
  }

  test() {
    console.log(
      "Test this.state.fileUploadOngoing=" + this.state.fileUploadOngoing
    );
    this.setState({
      fileUploadOngoing: true
    });

    const fileInput = document.querySelector("#fileInput");
    const formData = new FormData();

    formData.append("file", fileInput.files[0]);
    formData.append("test", "StringValueTest");

    const options = {
      method: "POST",
      body: formData
      // If you add this, upload won't work
      // headers: {
      //   'Content-Type': 'multipart/form-data',
      // }
    };
    fetch("http://localhost:5000/ui/upload/file", options);
  }
  render() {
    console.log("this.state.fileUploadOngoing=" + this.state.fileUploadOngoing);
    return (
      <div>
        <input id="fileInput" type="file" name="file" />
        <Button onClick={this.test} variant="primary">
          Primary
        </Button>

        {this.state.fileUploadOngoing && (
          <div>
            <h1> File upload ongoing abc 123</h1>
            {console.log(
              "Why is it printing this.state.fileUploadOngoing=" +
                this.state.fileUploadOngoing
            )}
          </div>
        )}

      </div>
    );
  }
}
Vest answered 22/1, 2020 at 23:3 Comment(0)
S
4

React File Upload Component

import { Component } from 'react';

class Upload extends Component {
  constructor() {
    super();
    this.state = {
      image: '',
    }
  }

  handleFileChange = e => {
    this.setState({
      [e.target.name]: e.target.files[0],
    })
  }

  handleSubmit = async e => {
    e.preventDefault();

    const formData = new FormData();
    for (let name in this.state) {
      formData.append(name, this.state[name]);
    }

    await fetch('/api/upload', {
      method: 'POST',
      body: formData,
    });

    alert('done');
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input 
          name="image" 
          type="file"
          onChange={this.handleFileChange}>
        </input>
        <input type="submit"></input>
      </form>
    )
  }
}

export default Upload;
Smelly answered 5/9, 2019 at 15:5 Comment(0)
C
1

I am working with the cloud was not able to push video same issues as above, added code below for the details and it worked fine for me. files[0] is the useState to store files and append data as array

var formData = new FormData()
formData.append('files', files[0])
for (var [key, value] of formData.entries()) {
  console.log(key, value)
}
fetch('url', {
  method: 'POST',
  body: formData,
  redirect: 'follow',
})
.then(res => console.log(res))

This solved my issue just adding redirect. Hope this might help.

Capello answered 7/3, 2023 at 12:14 Comment(0)
M
0

the request was rejected because no multipart boundary was found".

When you send multipart/form-data, the boundary is automatically added to a content-type of a request header. you have to tell the server when the parameter ends with the boundary rule. You had to set the Content-type like this

"Content-Type": `multipart/form-data: boundary=add-random-characters`

This article with guide you: https://roytuts.com/boundary-in-multipart-form-data/

The boundary is included to separate name/value pair in the multipart/form-data. The boundary parameter acts like a marker for each pair of name and value in the multipart/form-data. The boundary parameter is automatically added to the Content-Type in the http (Hyper Text Transfer Protocol) request header.

Macintosh answered 20/5, 2022 at 3:11 Comment(0)
D
0

A cleaner way:

function handleUpload(e: React.FormEvent<HTMLFormElement>) {
  e.preventDefault();  
  const formData = new FormData(e.target as HTMLFormElement);
  fetch(..., {body: formData})
}

You need to name the input as file as well as the type:

<form onSubmit={handleUpload}>
  <input type="file" name="file" />
  <button>Upload</button>
</form>
Detinue answered 29/2, 2024 at 9:38 Comment(0)
D
-1

For sending multipart/formdata, you need to avoid contentType, since the browser automatically assigns the boundary and Content-Type.

In your case by using fetch, even if you avoid Content-Type it sets to default text/plain. So try with jQuery ajax. which removes the contentType if we set it to false.

This is the working code

var data = new FormData();
var imagedata = document.querySelector('input[type="file"]').files[0];
data.append("data", imagedata);
$.ajax({
    method: "POST",
    url: fullUrl,
    data: data,
    dataType: 'json',
    cache: false,
    processData: false,
    contentType: false
}).done((data) => {
    //resolve(data);
}).fail((err) => {
    //console.log("errorrr for file upload", err);
    //reject(err);
});
Dafna answered 12/1, 2017 at 10:38 Comment(5)
Example in jQuery for ReactJS problem is just missing the point.Northeasterly
@Northeasterly You can use jQuery ajax in React using import $ from 'jquery'. Hope this makes sense.Dafna
I don't think he wants to use jQuery for this @JyothiBabuArajaEduce
youre just trying to badly solve cors issues with these suggestions.Gruber
@DevDonkey, Sorry I just given an approach to send multipart form data through XHR. Nowhere I'm solving a CORS issue here. Always open for edits if you have any.Dafna

© 2022 - 2025 — McMap. All rights reserved.