Kelen Luana Zimermann Kelen Luana Zimermann - 5 days ago 6
Java Question

Spring and Angular2 400 (Bad Request)

I am getting this 400 (Bad Request) when uploading a file to Spring.

The upload works fine on postman but is not that good on Angular2.

This is the postman code. Worked fine..
I used Basic Auth and Username + Password

var data = new FormData();
data.append("uploadfile", "pasta2.zip");

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
console.log(this.responseText);
}
});

xhr.open("POST", "http://localhost:8080/api/uploadFile");
xhr.setRequestHeader("authorization", "Basic b3BlcmF0aW9uczpvcGVyYXRpb25z");
xhr.setRequestHeader("cache-control", "no-cache");
xhr.setRequestHeader("postman-token", "59d5ebf2-6039-e924-1550-c96e491f97ee");

xhr.send(data);


This is my Spring Controller. It simple gets the file and save it to Disk.

@RestController
public class uploadFile {

private Logger logger = LoggerFactory.getLogger(this.getClass());
public String filenameZip, directoryZip;


@RequestMapping(value = "/api/uploadFile", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<?> uploadFile(
@ModelAttribute("uploadfile") MultipartFile uploadfile) {




try {
// Get the filename and build the local file path (be sure that the
// application have write permissions on such directory)
String filename = uploadfile.getOriginalFilename();
String directory = "C://Develop//files";
String filepath = Paths.get(directory, filename).toString();

filenameZip = "c:/Develop/files/"+filename;
directoryZip = "c:/Develop/files";

// Save the file locally
BufferedOutputStream stream =
new BufferedOutputStream(new FileOutputStream(new File(filepath)));
stream.write(uploadfile.getBytes());
stream.close();

} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
unzip(filenameZip, directoryZip);
return new ResponseEntity<>(HttpStatus.OK);
} // method uploadFile


This is my Angular2:
The is more code above. If you need I can provide it.

/**
* File upload
* Upload a Zip file to server.
*/

filesToUpload: Array<File>;


/**
* FormData gets the file as an Object and Post it on xhr with Auth
* Upload a Zip file to server.
*/

upload() {
this.makeFileRequest("http://localhost:8080/api/uploadFile", [], this.filesToUpload).then((result) => {
console.log(result);
}, (error) => {
console.error(error);
});
}

fileChangeEvent(fileInput: any){
this.filesToUpload = <Array<File>> fileInput.target.files;
}


makeFileRequest(url: string, params: Array<string>, files: Array<File>) {
return new Promise((resolve, reject) => {
var formData: any = new FormData();
var xhr = new XMLHttpRequest();
for(var i = 0; i < files.length; i++) {
formData.append("uploads[]", files[i], files[i].name);
}
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
resolve(JSON.parse(xhr.response));
} else {
reject(xhr.response);
}
}
}
xhr.open("POST", url, true);


/**
* Must set the Authorization or the Spring MVC does not accept the request. Tested on Postman
*/
xhr.setRequestHeader('Authorization', 'Basic ' + btoa("operations" + ":" + "operations"));

xhr.withCredentials = true;
xhr.send(formData); //Form data is sent to Spring
});
}


The log from spring when I upload a file from my App:

2016-11-27 19:14:57.862 INFO 3596 --- [io-8080-exec-10] o.e.ws.service.AccountServiceBean : > findByUsername
2016-11-27 19:14:57.875 INFO 3596 --- [io-8080-exec-10] o.e.ws.service.AccountServiceBean : < findByUsername
2016-11-27 19:14:57.986 INFO 3596 --- [io-8080-exec-10] o.s.b.a.audit.listener.AuditListener : AuditEvent [timestamp=Sun Nov 27
19:14:57 UTC 2016, principal=operations, type=AUTHENTICATION_SUCCESS, data={details=org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null}]
null


The log from Spring when I use the Postman app:

2016-11-27 19:16:12.762 INFO 3596 --- [nio-8080-exec-1] o.e.ws.service.AccountServiceBean : > findByUsername
2016-11-27 19:16:12.851 INFO 3596 --- [nio-8080-exec-1] o.e.ws.service.AccountServiceBean : < findByUsername
2016-11-27 19:16:12.965 INFO 3596 --- [nio-8080-exec-1] o.s.b.a.audit.listener.AuditListener : AuditEvent [timestamp=Sun Nov 27
19:16:12 UTC 2016, principal=operations, type=AUTHENTICATION_SUCCESS, data={details=org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null}]
Unzipping to c:\Develop\files\pasta2\dsa.txt


On Chrome it says:

zone.js:1382 POST http://localhost:8080/api/uploadFile 400 (Bad Request)


I believe this is not sending the zip file with the request.

Answer

Since you can accomplish what you want via Postman, I can safely assume that your problem is not in the backend. The problem is that you are not attaching any Multipart file in your request, in the frontend (Angular part of the code).

Manipulating the XHR Request is kind of a sketchy way to do this and you do not need to do it since Angular's final release.

A good way to do this is to create a dedicated service to perform the file upload:

import { Injectable } from '@angular/core';

import {Http, Headers, Response} from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class UploadService {

    //Import your APIs endpoint
    private url = AppSettings.API_ENDPOINT;

    constructor(private http:Http) { }

    upload(files:FileList){

        var formData = new FormData();


        if(files.length > 0){

            for(var i = 0; i < files.length; i++){
                formData.append("uploadfile", files[i]);
            }
            return this.http
                .post(this.url + '/api/upload', formData)
        }

    }
}

Then just subscribe to the service in your component

uploadDocs($event:any){
    console.log("IMPORT")

    var files:any = $event.target.files;
    console.log(files);

    this.uploadService.uploadLibraries(files)
            .subscribe(data => this.successImport(data),
            error => this.failImport(error));
}
Comments