Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with MultipartFile Upload From React/Node

I have the following JAVA controller:

    @RequestMapping(value = "/data/upload", method = RequestMethod.POST)
    public
    @ResponseBody
    void uploadData(@RequestParam("file") MultipartFile file) throws IOException {
        logger.info("Received File for Ingestion");
        dataUploadService.processData(file.getInputStream());
    }

Node server side code:

serviceCall(serviceCallRequestData, request, finalResponse) {
        logger.info('Making remote request: ' + JSON.stringify(serviceCallRequestData));

        let file = request.files['file']; // file: Object {name: "sample_aspect_review_upload.csv", encoding: "7bit", mimetype: "text/csv", mv: }
        let formData = new FormData();
        formData.append('file', Buffer.from(file.data));

        fetch(serviceCallRequestData.url, {
            method: serviceCallRequestData.requestObject.method,
            headers: serviceCallRequestData.requestObject.headers,
            body: formData
        }).then(response => {
            if (response.status !== 200) {
                logger.error(`Error while making http call requestData: ${JSON.stringify(serviceCallRequestData)}`);
                finalResponse.status(500).send('Internal server error');
                return;
            }

            return response.json();
        }).then((json) => {
            logger.info(`Returning response for aspect-review-file-upload: ${JSON.stringify(json)}`);
            finalResponse.status(200).send(JSON.stringify(json));
        }).catch((e) => {
            logger.error(`Error while making http call requestData: ${JSON.stringify(serviceCallRequestData)} error: ${JSON.stringify(e)}`);
            finalResponse.status(500).send('Internal server error');
        });
    }

Trying to upload a csv file like:

"product_id","review_id","aspect_id","rating","confidence_score","indices"
"pid","rid","aid",1,0.851955,"[{\"s\":0,\"e\":114,\"highlights\":[39,68]}]"

The upload happens easily from POSTMAN. See the below screenshot: enter image description here

Getting an error in JAVA: Received Unknown exception org.springframework.web.bind.MissingServletRequestParameterException: Required MultipartFile parameter 'file' is not present when I am not passing contentType header from React.

Getting an error in JAVA: org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found when I am passing contentType header from React as 'Content-Type': 'multipart/form-data'

The same Node server side code for a JAVA dropwizard controller as:

    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.APPLICATION_JSON)
    public Response uploadFile(
            @FormDataParam("file") InputStream inputStream,
            @FormDataParam("file") FormDataContentDisposition fileDetail
    ) throws IOException {
        logger.debug("Request to upload data file-name: {}", fileDetail.getName());

        dataUploadService.processData(inputStream);
        return Response.ok().build();
    } 

works correctly.

What am I doing wrong?

like image 662
User_Targaryen Avatar asked May 24 '18 09:05

User_Targaryen


2 Answers

From curl of postman you can see the following command:

curl -i -X POST -H "Content-Type:multipart/form-data" -F "file=@\"/pathto/sample.csv\";type=text/csv;filename=\"sample.csv\"" 'http://localhost:8080/data/upload'

So i tried adding type=text/csv;filename=\"sample.csv\" in the form-data so this should work:

    let file = request.files['file'];
    let formData = new FormData();
    formData.append("file", Buffer.from(file.data), {
        filename: file.name,
        contentType: file.mimetype,
    });

    fetch(serviceCallRequestData.url, {
        method: serviceCallRequestData.requestObject.method,
        headers: serviceCallRequestData.requestObject.headers,
        body: formData
    }).then(response => {
        if (response.status !== 200) {}
    });
like image 196
Prajit Nadkarni Avatar answered Oct 22 '22 03:10

Prajit Nadkarni


Few speculations:

  1. In your REST endpoint definition you specify file to be request parameter which is used to extract parameters from the URI itself, while multipart file upload sits within the body of the POST request.

    Add consumes={} and instead of @RequestParam you can use @RequestBody annotation:

    @RequestMapping(value = "/data/upload", method = RequestMethod.POST,
      consumes = {"multipart/mixed", "multipart/form-data"}
    )
    void uploadData(@RequestBody MultipartFile file) //body will be the whole JSON payload
    

    OR even better use @RequestPart where you can name the element:

    @RequestMapping(value = "/data/upload", method = RequestMethod.POST,
      consumes = {"multipart/mixed", "multipart/form-data"}
    )
    void uploadData(@RequestPart (value="file") MultipartFile file) //gets file from JSON
    
  2. Provide options to FormData:

    form.append('file', Buffer.from(file.data), {
      filename: 'image1.jpg', // ... OR:
      filepath: 'photos/image1.jpg',
      contentType: 'image/jpeg',
      knownLength: 19803
    });
    
    form.submit('http://example.com/', function(err, res) {
      if (err) throw err;
      console.log('Done');
    });
    

Can you confirm (using something like WireShark) what exactly gets sent from React?

like image 39
diginoise Avatar answered Oct 22 '22 01:10

diginoise