I'm trying to upload a file recently downloaded with Angular2 to Spring API Rest.
The problem is (display on spring app)...
The request was rejected because no multipart boundary was found
at
org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.(FileUploadBase.java:831)
~[tomcat-embed-core-8.5.28.jar:8.5.28] at
org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:256)
~[tomcat-embed-core-8.5.28.jar:8.5.28] at
org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:280)
~[tomcat-embed-core-8.5.28.jar:8.5.28] at
org.apache.catalina.connector.Request.parseParts(Request.java:2869)
~[tomcat-embed-core-8.5.28.jar:8.5.28] at
org.apache.catalina.connector.Request.parseParameters(Request.java:3216)
~[tomcat-embed-core-8.5.28.jar:8.5.28] at
org.apache.catalina.connector.Request.getParameter(Request.java:1137)
~[tomcat-embed-core-8.5.28.jar:8.5.28]
On the client side, the request is sent with "multipart/form-data" as content-type.
How can I fix it ?
fileDownloaderService
upload(file) {
const formData = new FormData();
formData.append('file', file);
const req = new HttpRequest('POST', this.urlUpload, file, {
headers: new HttpHeaders({'Content-Type':'multipart/form-data'}),
reportProgress: true
});
return this.http.request(req);
}
app.component
upload() {
let file = this.generate_dummy_file(50000000);
this.downloader.upload(file).subscribe( event => {
if (event.type === HttpEventType.UploadProgress) {
} else if (event instanceof HttpResponse) {
console.log('File is completly uploaded!');
}
});
}
generate_dummy_file(size) {
return new Blob([new ArrayBuffer(size)], {type: 'multipart/form-data'});
};
And spring side
#PostMapping("/uploadFile")
public UploadFileResponse uploadFile(#RequestParam("file") MultipartFile file) {
return ...;
}
Thanks for helping
Try this ,
const formData = new FormData();
formData.append("file", file);
formData.append("reportProgress", true);
use httpclient,
return this.httpclient.post(this.urlUpload, formData);
Have you set Content-Type? If so, please remove it.
headers:{
"Content-Type":"multipart/form-data", // remove it
},
Front End:
const formData = new FormData();
formData.append("file", file);
formData.append("reportProgress", "true");
return this.http.post<void>(this.API_URL + '/upload', formData);
backend end :
#Operation(description = "Upload File")
#ApiResponses(value = {
#ApiResponse(responseCode = "201", description = "File Uploaded successfully"),
#ApiResponse(responseCode = "400", description = "Problem during file upload ")
})
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#ResponseStatus(value = HttpStatus.CREATED)
public void upload(#RequestPart("file") final MultipartFile file) {
//action;
}
Are you actually sending FormData to your server?
Try wrapping the file in a FormData object for the HttpClient to automatically add the multipart boundaries.
upload(file) {
const formData = new FormData();
formData.append('file', file);
const req = new HttpRequest('POST', this.urlUpload, formData, {
headers: new HttpHeaders({'Content-Type':'multipart/form-data'}),
reportProgress: true
});
return this.http.request(req);
}
Related
I am trying to send a Json string along with multiple files into my Spring Controller, however it would always give me a 405 Method Not Allowed Error, what am I doing wrong?
Javascript Code:
var formdata = new FormData();
formdata.append('user', JSON.stringify(userData));
files.forEach(file=> {
formdata.append('files', file);
});
jQuery.ajax({
url: "user/submitForm",
type: "POST",
data: formdata,
enctype: 'multipart/form-data',
processData: false,
contentType: false,
success: function (data)
{
console.log("SUCCESS");
},
error: function (request, status, error) {
alert(status + " : " + JSON.stringify(request));
}
});
Controller in Spring:
#PostMapping(value= "/submitForm", consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE })
public ResponseEntity<?> userRegistration( #RequestPart("user") String user,
#RequestPart("files") List<MultipartFile> files, BindingResult bindingResult) {
ObjectMapper obj = new ObjectMapper();
User newUser = new User();
newUser = obj.readValue(user, User.class);
System.out.println("User : \n"+ newUser.toString());
System.out.println("Files : \n"+ files.toString());
return null;
}
This was the solution that I found from Antonio112009's answer
SOLUTION
#PostMapping(value = "/submitForm")
public ResponseEntity<?> userRegistration(
#RequestParam("user") String user,
#RequestParam(value = "files", required = false) List<MultipartFile> files) {
ObjectMapper obj = new ObjectMapper();
User userObj = new User();
.
.
.
}
I use another solution, who works as expected and are a bit more flexible from my point of view.
Front-end part is in Typescript.
Front-end
var formData = new FormData();
options.files.forEach(function (file) {
formData.append(file.name, file);
});
formData.append("myParam", "coucou");
var xhr = new XMLHttpRequest();
xhr.open("POST", "/rest/upload");
xhr.onload = function () {
var data = JSON.parse(xhr.responseText);
options.callback("success", options.files.map(function (file) {
return {
file: file,
content: data[file.name]
};
}));
};
xhr.send(formData);
Back-end (Java Spring)
#RestController
#RequestMapping(value = "/rest")
public class UploadController {
#PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Boolean> upload(MultipartHttpServletRequest request) {
// Get param
Object myParam = request.getParameter("myParam");
// Get iteretaor on all files
Iterator<String> iterator = request.getFileNames();
MultipartFile multipartFile = null;
while (iterator.hasNext()) {
multipartFile = request.getFile(iterator.next());
final String fileName = multipartFile.getOriginalFilename();
final String fileSize = String.valueOf(multipartFile.getSize());
// Add logic ....
}
}
return new ResponseEntity(true);
}
I need to develop a REST API endpoint to serve a file. So I implement the following code with help of other resources
#GetMapping(path = Array("/download"))
def downloadFile() : ResponseEntity[Resource] = {
var file: File = new File("src/main/scala/testFile.txt")
if(file.exists()){
val path = Paths.get(file.getAbsolutePath)
val resource = new InputStreamResource(new FileInputStream(file))
// val resource = new ByteArrayResource(Files.readAllBytes(path))
val responseHeaders = new HttpHeaders();
responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=results.txt")
responseHeaders.add("Cache-Control", "no-cache, no-store, must-revalidate")
responseHeaders.add("Pragma", "no-cache")
responseHeaders.add("Expires", "0")
println(resource)
ResponseEntity.ok()
.headers(responseHeaders)
.contentLength(file.length())
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(resource);
}else{
ResponseEntity.notFound().build()
}
}
and also from react side implemented as below
var win = window.open('_blank');
var filePath = ".../download"
downloadFile(filePath, function (blob) {
var url = URL.createObjectURL(blob);
win.location = url;
});
function downloadFile(url, success) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (success) success(xhr.response);
}
};
xhr.send(null);
}
the exception is coming from the scala side it throws
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class org.springframework.core.io.InputStreamResource] with preset Content-Type 'application/octet-stream']
any suggestions for fix this issue. thank you
I have an HTML form like this to upload files from front-end to back-end and do some operations:
<button mat-raised-button color="primary" type="button" style='margin-right:20px' (click)="selectFile()">Select File To Upload</button>
<input #fileUploadInput type="file" id="fileUpload" hidden name="avatar" (change)="fileChangeEvent($event)">
<button mat-raised-button color="primary" type="button" style='margin-right:20px' enctype="multipart/form-data" (click)="uploadFile()">Submit</button>
<br><br>
<a class="generate-full-width" style="color: darkred;" *ngIf="fileName"><strong>{{fileName}}</strong></a>
The component.ts is:
export class uploadFileDialog {
constructor(
public dialogRef: MatDialogRef<AddProductDialog>,
private uploadService: UploadService,
private builder: FormBuilder, public dialog: MatDialog,
#Inject(MAT_DIALOG_DATA) public data) {
}
#ViewChild('fileUploadInput', {static: false})
fileUploadVariable: ElementRef;
fileName;
currentFile: File;
filesToUpload = [];
resetFile(){
this.fileUploadVariable.nativeElement.value = "";
}
selectFile(){
this.resetFile();
let el: HTMLElement = this.fileUploadVariable.nativeElement as HTMLElement;
el.click();
}
fileChangeEvent(fileInput: any) {
let file = fileInput.target.files[0]
console.log(file)
//console.log(file.data.toString());
this.filesToUpload = [];
this.filesToUpload.push(file);
this.fileName = file['name'];
}
uploadFile(){
this.currentFile = this.fileName;
console.log(this.currentFile);
this.uploadService.uploadFile(this.currentFile)
.subscribe((data) => {
console.log(data)
},
error => {
console.log(error)
});
}
}
Service.ts is:
uploadFile(file: File): Observable<any> {
let headers = new HttpHeaders({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
//'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization'
'Access-Control-Allow-Headers': 'Content-Type,Accept,X-Access-Token,X-Key,Authorization,X-Requested-With,Origin,Access-Control-Allow-Origin,Access-Control-Allow-Credentials,content-type=multipart/*'
})
let options = {headers:headers, observer: 'response'};
const formData: FormData = new FormData();
formData.append('file', file);
//return this.http.post(this.url+'/fileUpload/upload', formData,options)
const req = new HttpRequest('POST', this.url+'/fileUpload/upload', formData, {
reportProgress: true,
responseType: 'json'
});
return this.http.request(req);
}
The controller file at Java backend is:
#RestController
#CrossOrigin(origins = "*", allowedHeaders="*", exposedHeaders="Access-Control-Allow-Origin")
#RequestMapping("/fileUpload")
public class FileController {
private final FileService fileService;
#Autowired
public FileController(FileService fileService) {
this.fileService = fileService;
}
#PostMapping(value = "/upload")
public void handleFileUpload(#RequestParam("file") MultipartFile file) throws IOException {
fileService.storeFile(file);
}}
and the Service File at Java Backend is:
#Service
public class FileService {
private static final String FILE_DIRECTORY = "D:\\temp";
public void storeFile(MultipartFile file) throws IOException {
Path filePath = Paths.get(FILE_DIRECTORY + "\" + file.getOriginalFilename());
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
}
}
I am able to see the file name when uploading in the console. Also, in the body of the request, the formData is showing the xml file as content in the Networks tab. I Java console, I am getting the error:
2020-12-15 12:26:53.144 WARN 9688 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present]
Error in front-end console:
HttpHeaderResponse {headers: HttpHeaders, status: 400, statusText: "OK", url: "http://localhost:8080/fileUpload/upload", ok: false, …}
headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
ok: false
status: 400
statusText: "OK"
type: 2
url: "http://localhost:8080/fileUpload/upload"
__proto__: HttpResponseBase
What am I doing wrong?
You are sending only file name but not actual file blob.
Try to do below changes,
component.ts :
uploadFile() {
this.currentFile = this.fileName;
console.log(this.currentFile, this.filesToUpload[0]);
this.uploadService.uploadFile(this.currentFile, this.filesToUpload[0])
.subscribe((data) => {
console.log(data)
},
error => {
console.log(error)
});
}
service.ts
uploadFile(fileName: string, file: File): Observable<any> {
let headers = new HttpHeaders({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type,Accept,X-Access-Token,X-Key,Authorization,X-Requested-With,Origin,Access-Control-Allow-Origin,Access-Control-Allow-Credentials,content-type=multipart/*'
})
let options = {headers:headers, observer: 'response'};
const formData: FormData = new FormData();
formData.append('fileName', fileName);
formData.append('file', file);
const req = new HttpRequest('POST', this.url+'/fileUpload/upload', formData, {
reportProgress: true,
responseType: 'json'
});
return this.http.request(req);
}
Please refer this link to know more about formData
I'm trying to download any file calling my rest webservices. I'm using spring + jersey for the web services and Angular 2 for the front.
So when I clink on the front, the webservices get my file but the window to download it is not shown.
My rest API :
#POST
#Path("/download")
#ApiOperation(value = "Download")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response downloadFile(#ApiParam(value = "File", required = true) String filePath) {
File file = new File("/webapps/pdf/upload/msg/1/gest.PNG");
Response.ResponseBuilder response = Response.ok((Object) file);
try {
String contentType = Files.probeContentType(file.toPath());
response.header("Content-Disposition", "attachment; filename="+file.getName());
response.header("Content-Type", contentType);
return response.build();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
My Angular service :
downloadFile(path) {
const headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded', 'Accept': '*'});
const options = new RequestOptions({headers: headers});
options.responseType = ResponseContentType.Blob;
return this.http.post(apiUrl + "msg/download", path, options)
.catch(this.handleError);
}
My Angular component :
downloadFile(documentPath) {
this.msgService.downloadFile(documentPath).subscribe(response => {
var contentType = response.headers('Content-Type');
let url = window.URL.createObjectURL(new Blob([response._body], {type: contentType}));
window.open(url);
});
}
Html :
<figure class="ui-g-12 " *ngFor="let document of msg.documents_path" (click)="downloadFile(document)">
<img [src]="selectImageByExtension(document.split('.').pop().toLowerCase())" />
<figcaption>{{document.split('/').pop().toLowerCase()}}</figcaption>
</figure>
When I click on my figure I can see that the file is well gotten:
But nothing pops up.
What did I miss ?
So the only solution working for me was to use GET request instead of POST passing the filepath as a pathparam.
Rest API :
#GET
#Path("/download/{filePath}")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getdownloadFile(#PathParam("filePath") String filePath) {
String path = null;
byte [] barr = Base64.getDecoder().decode(filePath);
path = new String(barr);
File file = new File(path);
try {
String contentType = Files.probeContentType(file.toPath());
Response.ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition", "attachment; filename="+file.getName());
response.header("Content-Type", contentType);
response.header("Content-Length", file.length());
return response.build();
} catch (IOException e) {
e.printStackTrace();
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
}
}
Angular service :
downloadFile(path) {
const headers = new Headers({'Content-Type': 'text/plain', 'Accept': '*'});
const options = new RequestOptions({headers: headers});
options.responseType = ResponseContentType.Blob;
return this.http.get(apiUrl + "msg/download/"+btoa(path), options)
.map(res => res)
.catch(this.handleError);
}
Angular component :
downloadFile(documentPath) {
this.msgService.downloadFile(documentPath).subscribe(response => {
let params = documentPath.split('/' );
var blob = new Blob([response._body]);
FileSaver.saveAs(blob, params[params.length-1]);
});
}
I'm trying to upload (actually POST) numerous small files in one go along with some key, value pairs:
$scope.uploadFiles = function(files) {
if (files.length === 0) {
return;
}
var formData = new FormData();
formData.append('keyName1', 'keyValue1');
formData.append('keyName2', 'keyValue2');
formData.append('keyName3', 'keyValue3');
for (var i = 0; i < files.length; i++) {
formData.append('files[]', files[i]);
}
$http.post( '/myEndpoint', formData, {
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function (result) {
console.log('YAY');
}).error(function () {
console.log('NAY');
});
}
Here's the Java backend:
#RequestMapping(value = "/myEndpoint", method = RequestMethod.POST)
#ResponseBody
public void uploadFiles(
#RequestParam("files") List<MultipartFile> fileList,
#RequestParam("keyName1") String keyName1,
#RequestParam("keyName2") String keyName2,
#RequestParam("keyName3") String keyName3,
HttpServletResponse response, HttpSession session) throws Exception {
log.debug(fileList.size()); // Always logs zero
}
The endpoint is being hit but the filesList length is zero. I've also changed
List<MultipartFile> fileList to MultipartFile[] filesArray
but that didn't work wither.
Can anyone shed some light please?
Cheers,
Paul
This might be helpful to you.
Angular:
$scope.uploadFiles = function(files) {
if (files.length === 0) {
return;
}
var formData = new FormData();
formData.append('keyName1', 'keyValue1');
formData.append('keyName2', 'keyValue2');
formData.append('keyName3', 'keyValue3');
for (var i = 0; i < files.length; i++) {
formData.append('file'+i, files[i]);
}
$http.post( '/myEndpoint', formData, {
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function (result) {
console.log('YAY');
}).error(function () {
console.log('NAY');
});
}
On Spring/Java Side:
RequestMapping(value = "/myEndpoint", method = RequestMethod.POST)
public #ResponseBody Object uploadFiles(MultipartHttpServletRequest request, HttpServletResponse response) throws IOException {
//do stuff here...
final String keyName1= request.getParameter('keyName1');
//and so on......
Iterator<String> iterator = request.getFileNames();
MultipartFile multipartFile = null;
while (iterator.hasNext()) {
multipartFile = request.getFile(iterator.next());
//do something with the file.....
}
}
BTW, on you angular side, you can always end the file on one go or with multiple request. It's up to you how you want that implemented.
I used a library on GitHub to help me accomplish this task with my Java Glassfish Server.
https://github.com/nervgh/angular-file-upload
I only needed to upload a single file, and here is the Java Backend to receive that file. This framework does have support to upload multiple files to the server.
#POST
#Path("/Endpoint")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response whateverEndPoint(#FormDataParam("fileName") InputStream fileInputStream,
#FormDataParam("fileName") FormDataContentDisposition contentDispositionHeader,
#FormDataParam("additionalParameter") String additionalParameter) {
System.out.println(contentDispositionHeader.getFileName());
String output = "File Received on the server: ";
return Response.status(200).entity(output).build();
}
Here is my angular controller that uses the framework:
angular.module('AppThing').controller('DemoController',function($rootScope,$scope,FileUploader){
//creating the uploader with the correct url
$scope.uploader = new FileUploader({
url : 'Endpoint',
method : 'POST',
alias: 'fileName',
autoUpload:true
});
//runs right after we add a file to the queue
$scope.uploader.onAfterAddingFile = function(item){
};
//runs right before we upload an item to the server
$scope.uploader.onBeforeUploadItem = function(item){
console.log('This is just before the image upload');
item.formData.push({'additionalParameter': $rootScope.additionalParameter});
};
$scope.uploader.onSuccessItem = function(item, response, status, headers) {
};
});
Hope this helps