1

I need to upload a photo to the server which has been written using Spring Boot. For the front end which sends the request I use Angular2.

This is my API which accepts the HTTP request. It works fine. (I tested it using Postman)

@RequestMapping(method = RequestMethod.POST, value = "/tender/save-new/save-photo")
public ResponseEntity<?> uploadPhoto(@RequestParam("file") MultipartFile file){

    if (file.isEmpty()) {
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setMessage("DEBUG: Attached file is empty");
        return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND);
    }
    String returnPath = null;
    try {
        // upload stuff
    } catch (IOException e) {
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setMessage(e.getMessage());
        return new ResponseEntity<ErrorResponse> (errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    return new ResponseEntity<String>(returnPath, HttpStatus.OK);
}

I am not sure how should I write the code in Angular2 to call the server. Following is what I have come up with.

 savePhoto(photoToSave: File) {

    let formData: FormData = new FormData();
    formData.append('file', photoToSave);

    let savedPath = this._http
        .post(this._endpointUrl + "tender/save-new/save-photo", formData)
        .map(
        res => {
            return res.json();
        }
        )
        .catch(handleError);

    return savedPath;
}

As you can see, I append the 'file' parameter to the form data before sending it. Server method accepts the RequestParam as 'file'.

But, in the server log, I get following error.

org.springframework.web.multipart.MultipartException: Current request is not a multipart request at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:190)

Note that I haven't declared a CommonsMultipartResolver bean since SprinBoot implicitly handles it. (I have heard this. Please correct if I am wrong.)

I think that the error comes up because of a missing value in the request. What Spring is saying by handleMissingValue? What am I missing?

vigamage
  • 1,975
  • 7
  • 48
  • 74

2 Answers2

1

you need to specify that your controller is expecting multipart

@RequestMapping(method = RequestMethod.POST, value = "/tender/save-new/save-photo", consumes = {"multipart/form-data"})
public ResponseEntity<?> uploadPhoto(@RequestParam("file") MultipartFile file){

Also to solve the CORS problem you need to add the methods you are planning to use in your cors mapping, try something like this

 @Configuration
 public class WebMvcConfiguration {

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD");
        }
    };
}
}
Amer Qarabsa
  • 6,412
  • 3
  • 20
  • 43
  • but according to following answer, Angular2 final supports file upload without xhr https://stackoverflow.com/a/39862337/3892439 – vigamage May 23 '17 at 08:34
  • hmm that is interesting, did not know it was supported, did you the second part of the solution where you put consumes = {"multipart/form-data"} in your request mapping? – Amer Qarabsa May 23 '17 at 08:38
  • yes. Now it gives me `` No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. `` . But the thing is I have put `@CrossOrigin(origins = "http://localhost:3000")` in my controller – vigamage May 23 '17 at 08:42
  • ok then your issue is resolved, this error related to CORS protocol which protect your controller from being accessed from another domain, i will edit my answer to solve this issue as well. – Amer Qarabsa May 23 '17 at 08:44
  • please feel free to accept the answer if it solved your issue – Amer Qarabsa May 23 '17 at 09:03
  • Problem continues with headers I think. I make `content-type` header to be `application/json` for every request using a `CustomRequestOptions` class which extends `BaseRequestOptions`. Do I have to make it `'multipart/form-data'`? – vigamage May 23 '17 at 09:11
  • No you do not have to make it multipart/form-data, actually there is a good chance to face issue if you do , but you absolutly should not have content type json – Amer Qarabsa May 23 '17 at 09:15
  • let me try removing content type json and try – vigamage May 23 '17 at 09:16
  • removing that affects my login request. I will get back to you once I find a way. Do you really believe that removing content-type = application/json would resolve this? – vigamage May 23 '17 at 09:22
  • yes you cannot tell the controller to expect json and sent it multipart – Amer Qarabsa May 23 '17 at 09:38
0

i had the same problem, this is the solution i am using right now:

for the back end using spring boot:

 @RequestMapping(value="/save", method = RequestMethod.POST)
public ResponseEntity<?> saveTemp(@RequestParam("file") MultipartFile file) throws Exception{
    String nomFichier=file.getOriginalFilename();

    try {
        byte[] bytes = file.getBytes();
        File fi=new File(tempRepo+nomFichier);
        fi.getParentFile().mkdirs();
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream((new FileOutputStream(fi)));
        bufferedOutputStream.write(bytes);
        bufferedOutputStream.close();



    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }
    return new ResponseEntity<>(HttpStatus.OK);
}

for the front end using Angular2:

public save(file:any){

 const formData = new FormData();
 formData.append("file", file);

 return this._http
  .post('http://localhost:8081/save', formData)
  .catch(this._errorhandler);
}
N. berouain
  • 1,181
  • 13
  • 17