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
Related
we have S3 storage ,there are a lot of some files, jpg,mp3 and others
what i need to do?
i need to redirect client to get the file from our s3 without uploading it on our server
and i want that clien get the file on his pc with name and extension
so it looks like clien send us uuid - we find link of this file on s3 and redirect it like this
#GetMapping("/test/{uuid}")
public ResponseEntity<Void> getFile(#PathVariable UUID uuid) {
var url = storageServiceS3.getUrl(uuid);
try {
var name = storageServiceS3.getName(uuid);
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY)
.header(HttpHeaders.LOCATION, url)
.header(HttpHeaders.CONTENT_TYPE, new MimetypesFileTypeMap().getContentType(name))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + name)
.build();
} catch (NoSuchKeyException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.build();
}
}
everything works good ,the file is downloading but one problem - the file has no name (its name still is key from s3) and no extension.
i think this code not works correctly
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + name)
is there any way to do this or i still need upload file to server and then send it to client ?
Finally i found solution- i use S3Presigner ,make presigned url and redirect it with simple Http response
#Bean
public S3Presigner getS3Presigner() {
return S3Presigner.builder()
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(ACCESS_KEY, SECRET_KEY)))
.region(Region.of(REGION))
.endpointOverride(URI.create(END_POINT))
.build();
}
public String getPresignedURL(UUID uuid) {
var name = getName(uuid);
var contentDisposition = "attachment;filename=" + name;
var contentType = new MimetypesFileTypeMap().getContentType(name);
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket(BUCKET)
.key(uuid.toString())
.responseContentDisposition(contentDisposition)
.responseContentType(contentType)
.build();
GetObjectPresignRequest getObjectPresignRequest =
GetObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(5))
.getObjectRequest(getObjectRequest)
.build();
PresignedGetObjectRequest presignedGetObjectRequest =
s3Presigner.presignGetObject(getObjectPresignRequest);
return presignedGetObjectRequest.url().toString();
}
#GetMapping("/redirect/{uuid}")
public void redirectToS3(#PathVariable UUID uuid, HttpServletResponse response) {
try {
var URI = storageServiceS3.getPresignedURL(uuid);
response.sendRedirect(URI);
} catch (NoSuchKeyException | IOException e) {
response.setStatus(404);
}
}
It works pretty good ;)
#Алексеев станислав
Some work arround for this is consuming your rest service by javascript and add file's name in a new header response and rename file when download by client.
// don't forget to allow X-File-Name header on CORS in spring
headers.add("X-File-Name", nameToBeDownloaded );
Example on ANGULAR but can be parsed to other language
this.http.get(uri_link_spring_redirecting_to_S3, {
responseType: 'blob',
observe: 'response'
}).subscribe(
(response) => {
var link = document.createElement('a');
var file = new Blob([response.body], {
type: 'text/csv;charset=utf-8;'
});
link.href = window.URL.createObjectURL(file);
link.download = response?.headers?.get('X-File-Name');; 'download.csv';
link.click();
},
error => {
...
}
)
I try to send an image with some attributes from flutter to spring boot endpoint, but spring boot not received the image at all and it gives me this error:
Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'image' is not present]
here is my code:
Spring boot
#PostMapping("")
public ResponseEntity<UsersEntity> createNewUser(#RequestParam(value = "image") MultipartFile image, UsersEntity user) {
UsersEntity response = userService.createUser(user, image);
return ResponseEntity.ok(response);
}
Flutter
var postUri = Uri.parse("http://localhost:8080/v1");
var request = new http.MultipartRequest("POST", postUri);
request.fields['fName'] = firstNameController.text;
request.fields['lName'] = lastNameController.text;
if(image != null) {
request.files.add(http.MultipartFile.fromBytes(
'image', image!, contentType: MediaType.parse('multipart/form-data')));
}
Map<String, String> headers = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Headers": "Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,locale",
"content-type": "application/json"};
request.headers.addAll(headers);
request.send().then((response) {
if (response.statusCode == 200) {
print("Success!");
} else {
print('Error!');
}
});
My service work very well with Postman, any idea how can I solve this issue?
Solution
Credit to this https://github.com/flutter/flutter/issues/37311#issuecomment-516967285
I just add filename and every thing work fine:
request.files.add(http.MultipartFile.fromBytes(
'image', image!,
contentType: MediaType.parse('multipart/form-data'),
filename: 'test.jpg'));
var uri = Uri.parse('$baseUrl/services/selfemployment/check-confirmation-code');
var request = new http.MultipartRequest("POST", uri);
request.headers.addAll(baseHeader);
request.fields['example'] = example;
request.fields['image'] = image;
http.Response response = await http.Response.fromStream(await request.send());
print('Uploaded! ${response.body} ++ ${response.statusCode}');
I'm trying to make a download of a ODS file (Open Office's Calc file).
I'm using Java for back end and AngularJS for front end.
This is my code of the front end where I receive and download the file:
vm.downloadModel = () => {
DataFactory.GET(`${URL.CTM_ODS()}/rotas/modelo`, { headers: { 'Content-Type': 'application/octet-stream' } }).then(response => {
let file = new Blob([response], { type: 'application/vnd.oasis.opendocument.spreadsheet' });
let url = window.URL || window.webkitURL;
let downloadLink = angular.element('<a></a>');
downloadLink.attr('href', url.createObjectURL(file));
downloadLink.attr('target', '_self');
downloadLink.attr('download', 'Model_Routes_OD.ods');
downloadLink[0].click();
});
}
This is my endpoint on my back end:
#GetMapping(value = "/modelo")
#ResponseBody
public ResponseEntity<ByteArrayResource> getModelo() throws IOException {
String path = "C:\\Model_Routes_OD.ods";
File file = new File(path);
Path pathObj = Paths.get(file.getAbsolutePath());
ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(pathObj));
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getName());
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(Files.readAllBytes(pathObj).length));
headers.add(HttpHeaders.TRANSFER_ENCODING, "binary");
return new ResponseEntity<>(resource, headers, HttpStatus.OK);
}
The file that is downloaded came corrupted! My file has 13Kb and when it downloads shows 21Kb, when I try to open he says that the file is "broken" and he can try to fix it but, without success.
I tried your code and api is working fine.
You don't need to explicitly handle file at frontend, instead you can just download it.
Example code for above scenario:
var app = angular.module('demo', []);
app.controller('Download', function($scope, $http) {
$scope.download = function(){
var url = "http://localhost:8080/modelo"
window.open(url, "_self");
}
});
I am able to download file perfectly and here is complete code for this example
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);
}
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]);
});
}