53

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?

9 Answers 9

31

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;
Sign up to request clarification or add additional context in comments.

6 Comments

Straight to the point! And thanks for the base64 addition.
Glad to know you like it @mrKindo
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-ac0674ade821
@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.
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 attribute
|
28

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!");
    });

2 Comments

how do you process this data in the backend? assuming php @PeterJiang
We did not use Php, but Java
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.

1 Comment

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'))
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>
    );
  }
}

Comments

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;

Comments

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.

Comments

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.

Comments

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>

Comments

-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);
});

5 Comments

Example in jQuery for ReactJS problem is just missing the point.
@lilly You can use jQuery ajax in React using import $ from 'jquery'. Hope this makes sense.
I don't think he wants to use jQuery for this @JyothiBabuAraja
youre just trying to badly solve cors issues with these suggestions.
@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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.