5

Spring Rest Controller

@PostMapping(
    value = "/post",
    produces = MediaType.APPLICATION_JSON_VALUE,
    consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}
)
public ResponseEntity<User> handleFileUpload(@RequestParam("user") User user, @RequestPart("file") MultipartFile file) {
    // do something with User and file
    return ResponseEntity.ok().build();
}

Angular Service

@Injectable()
export class UploadFileService {

  constructor(private http: HttpClient) { }
  pushFileToStorage(file: File): Observable<HttpEvent<{}>> {
    let formdata: FormData = new FormData();
    formdata.append('file', file);
    formdata.append('user', JSON.stringify(new User('John', 12)))

    const req = new HttpRequest('POST', '/post', formdata, {
      reportProgress: true,
    });

    return this.http.request(req);
  }
}

When I try to send the request I get 500 Internal Server Error.

Here's a request header

POST /post HTTP/1.1
Host: localhost:4200
Connection: keep-alive
Content-Length: 152881
Accept: application/json, text/plain, */*
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydaQb5yaWw2xu1V9r
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9

Request payload

------WebKitFormBoundarydaQb5yaWw2xu1V9r
Content-Disposition: form-data; name="file"; filename="Screen Shot 2017-10-24 at 8.49.13 PM.png"
Content-Type: image/png


------WebKitFormBoundarydaQb5yaWw2xu1V9r
Content-Disposition: form-data; name="user"

{"name":"John","age":12}
------WebKitFormBoundarydaQb5yaWw2xu1V9r--

Note: If in Spring rest controller I change parameter type User to String it works.

Question: How to send request from Angular so that on Spring I can get User and MultipartFile, instead of String.

Guillaume Georges
  • 3,878
  • 3
  • 14
  • 32
A0__oN
  • 8,740
  • 6
  • 40
  • 61

1 Answers1

9

Changing

public ResponseEntity<User> handleFileUpload(@RequestParam("user") User user, @RequestPart("file") MultipartFile file)

to

public ResponseEntity<User> handleFileUpload(@RequestPart("user") User user, @RequestPart("file") MultipartFile file)

and changing the request to something like this will work:

curl -i -X POST -H "Content-Type: multipart/form-data" \
-F 'user={"name":"John","age":12};type=application/json' \
-F "file=@myfile.txt" http://localhost:8080/post

For consumes only MediaType.MULTIPART_FORM_DATA_VALUE is required.

To make above kind of request in angular, something like this can be done:

const userBlob = new Blob(JSON.stringify(new User('John', 12)),{ type: "application/json"});
formdata.append('user', userBlob);
Saheb
  • 1,392
  • 3
  • 13
  • 33
  • I tried the same, but I'm getting, Failed to convert request element: org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException: Failed to convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'com.PersonDTO'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'com.PersonDTO': no matching editors or conversion strategy found – Tharaka May 20 '18 at 16:45
  • @dinesh Can you post your code in a separate question? – Saheb May 21 '18 at 15:57
  • 1
    ,Please refer the below.. https://stackoverflow.com/questions/50462894/upload-a-json-object-user-information-and-multiple-image-with-single-request – Tharaka May 22 '18 at 08:16
  • 4
    I tried your proposition, you need to add `[]` to `JSON.stringify(new User('John', 12)`: `const myObjStr = JSON.stringify(new User('John', 12); const userBlob = new Blob([myObjStr],{ type: "application/json"});` – Ousama Oct 13 '20 at 16:21
  • It depends upon how you want your JSON and the data model used at the backend. Both with or without `[]` are valid JSON and should work fine if we have the right data model to handle it. – Saheb Oct 14 '20 at 01:27