Download file from rest webservices spring - java

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]);
});
}

Related

How to redirect s3 link from rest controller java

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 => {
...
}
)

Download File with Spring with Scala

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

Download ODS/XLS from Rest API Java

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

file download via http post is returning the zip file contents

I could see many related topics, but I have a specific problem. I am using spring boot controller to download a zip file. I am able to download the file when it is http verb get, but as I have to pass a big json payload I changed to post. Since then instead of downloading it as file it is responding the contents of the file with some ascii characters. Below is the method in controller for downloading the file.
#ApiResponses(value = { #ApiResponse(code = 404, message = "file could not be found"),
#ApiResponse(code = 200, message = "File was created sucessfully") })
#PostMapping(path="/download-file/1.0", produces="application/zip")
public ResponseEntity<InputStreamResource> downloadFile(
#ApiParam(value = "File creation contents", required = true) #RequestBody InputDetailsVO inputDetailsVO) {
File file = null;
InputStreamResource resource = null;
HttpHeaders headers = new HttpHeaders();
try {
//Creating InputStreamResource out of zip file
resource = new InputStreamResource(new FileInputStream(file));
String contentType = "application/zip";
if (!StringUtils.isEmpty(contentType)) {
headers.setContentType(MediaType.parseMediaType(contentType));
}
headers.add("Content-Disposition","attachment; filename=\""+file.getName()+"\"");
} catch (Exception e) {
log.error("Issue with file creation",e);
}
return ResponseEntity.ok()
.contentLength(file.length())
.contentType(MediaType
.parseMediaType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
.headers(headers).body(resource);
}
Below is the response I am getting instead of file download
PK;��N <?xml version="1.0" encoding="UTF-8"?>
<employeeDetails>
<name>Harry</name>
<age>30</30>
<email>test#test.com</test>
</employeeDetails>PK�qB�#Y;YPK;��N�qB�#Y;Yemployee details.xmlPKL�Y
Try like this, you can download any type of file. I assume that InputDetailsVO contains the name of the file or you can have your own logic to pick the file name. On the top of this method, you can provide swagger related annotations.
#PostMapping(value = "/download-file/1.0", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<?> downloadFile(#RequestBody InputDetailsVO inputDetailsVO) {
String dirPath = "your-location-path";
byte[] fileBytes = null;
try {
String fileName = inputDetailsVO.getFileName();
fileBytes = Files.readAllBytes(Paths.get(dirPath + fileName));
} catch (IOException e) {
e.printStackTrace();
}
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.body(fileBytes);
}
I also had a similar use case. I am sharing the code which had solved the issue.
#RequestMapping(value="/download",method=RequestMethod.GET,produces="application/zip" )
public ResponseEntity<?> download(HttpServletResponse response) throws IOException
{
//Some Code...
File file = new File("F:\\Folder\\Folder\\Folder\\"+filename);
InputStreamResource resource2 = new InputStreamResource(new FileInputStream(file));
response.setContentType("application/zip");
response.setHeader("Content-Disposition", String.format("inline; filename=\"" + filename + "\""));
response.setHeader("responseType", "arraybuffer");
response.setHeader("Content-Length", ""+file.length());
return new ResponseEntity<InputStreamResource>(resource2,HttpStatus.ACCEPTED);
}

File is being saved with Content-Disposition header

I have angular2 front-end and Dropwizard back-end. I'm trying to upload a picture from front-end to back-end.
My html code:
<input type="file" name="file" (change)="fileChange($event)">
My component:
fileChange(event) {
let fileList: FileList = event.target.files;
if (fileList.length > 0) {
let file: File = fileList[0];
let formData:FormData = new FormData();
formData.append('file', file);
this.siteDescriptionService.sendPhoto(formData).subscribe(value => {
console.log("value", value);
});
}
}
My service:
sendPhoto(data): Observable<any> {
return this.http.postPhoto('api/site/savePhoto', data, null).map(res => res);
}
My http interceptor:
postPhoto(url: string, params?: any, options?: RequestOptionsArgs): Observable<any> {
this.beforeRequest();
let headers = new Headers();
headers.append('Content-Type', 'multipart/form-data');
let reqOptions = new RequestOptions({ headers: headers });
return super.post(this.getFullUrl(url), params, reqOptions)
.catch(this.onCatch)
.do((res: Response) => {
this.onSuccess(res);
}, (error: any) => {
this.onError(error);
})
.finally(() => {
this.onFinally();
});
}
The request is being send with such payload:
------WebKitFormBoundaryAz4AnN4lFPWKUvmH
Content-Disposition: form-data; name="file"; filename="logo.png"
Content-Type: image/png
------WebKitFormBoundaryAz4AnN4lFPWKUvmH--
And on my server I have:
#POST
#Timed
#Path("savePhoto")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
public Response uploadFile(InputStream uploadedInputStream) throws IOException {
String uploadedFileLocation = "/tmp/photo1.png";
FormDataMultiPart part = new FormDataMultiPart().field("file", uploadedInputStream, MediaType.TEXT_PLAIN_TYPE);
FormDataBodyPart p = part.getField("file");
InputStream i = (InputStream) p.getEntity();
writeToFile( i, uploadedFileLocation);
String output = "File uploaded to : " + uploadedFileLocation;
return Response.ok(output).build();
}
private void writeToFile(InputStream uploadedInputStream, String uploadedFileLocation)
throws IOException {
int read;
final int BUFFER_LENGTH = 1024;
final byte[] buffer = new byte[BUFFER_LENGTH];
OutputStream out = new FileOutputStream(new File(uploadedFileLocation));
while ((read = uploadedInputStream.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
out.flush();
out.close();
}
Everything is fine, file is being saved, but it's saved with the whole request payload, including Content-Disposition, Content-Type headers, etc., and therefore the file becomes "broken".
How can I remove the Content-Disposition header from file?
When you use an InputStream parameter, you're saying you want to whole request body. If you just want a single part, you need to annotate it with #FormDataParam with the name of the part
public Response uploadFile(#FormDataParam("file") InputStream file,
#FormDataParam("file") FormDataContentDisposition fdcd) {
String filename = fcdc.getFileName();
// You don't need to create the FormDataMultiPart
// just save the InputStream parameter
}
In order for this to work, you also need to to register the MutliPartFeature
env.jersey().register(MultiPartFeature.class);

Categories