I am working a spring boot server with an angular4 front page. I have a service to download a .zip file from my front using HttpClient. Here's my code :
Angular service:
getZip(fileName: string) : Observable<any> {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/zip',
'Accept': 'application/zip'
}),
params: new HttpParams().set('fileName', fileName),
reponseType: 'blob'
};
return this.http.get<Blob>(extractZip, httpOptions);
}
Angular service call :
this.myService.sendSql(this.sql, this.dataSource, this.fileGeneration).subscribe(data => {
if(this.fileGeneration) {
this.myService.getZip(data.zipName).subscribe(blob => {
console.log(blob);
console.log("Zip file download success.")
},
error => {
console.log(error);
console.log("Zip file download failed.")
});
}
},
err => {
console.log('An error occured when contacting application server.');
});
So basically I use this.myService.sendSql() to get the zip name that I will use with this.myService.getZip() to download the zip.
My request is like that : http://localhost:8870/extracts_sql?fileName=name.zip and inside my browser it works perfectly.
Here the server side code :
#GetMapping("/extracts_sql")
public ResponseEntity<InputStreamResource> getFile(#RequestParam String fileName) throws FileNotFoundException {
Configuration configuration = ConfigurationHelper.readConfiguration(configurationFile);
MediaType mediaType = MediaTypeUtils.getMediaTypeForFileName(this.servletContext, fileName);
File file = new File(configuration.getProcessingFolder() + File.separatorChar + fileName);
InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
//Resource file = this.sqlToolService.loadFile(fileName, configuration);
log.i("Sending zip :"+fileName+" to user.");
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.contentType(mediaType)
.contentLength(file.length())
.body(resource);
}
The issue is that I get a HttpErrorResponse on the angular side even though its statuscode = 200 , here's the error : SyntaxError: Unexpected token P in JSON at position 0 at JSON.parse Http failure during parsing for http://localhost:8870/extracts_sql?fileName=name.zip
Any ideas ?
return this :
return this.http.get<Blob>(extractZip, httpOptions)
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'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 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 have my REST response set up like so:
#MethodMapping(value = "/download", httpMethod = HttpMethod.GET)
public Response getLogFile() {
File log = new File("path");
if (log.exists())
{
return Response.ok(log, MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment; filename=\"" + log.getName() + "\"" ) //optional
.build();
}
}
But the link just gives me a text response rather than a download:
{"status":200,"entity":{"path":"path"},"metadata":{"Content-Type":["application/octet-stream"],"Content-Disposition":["attachment; filename\u003d\"proteus.log\""]},"entityClosed":false,"entityBufferred":false}
The two packages I'm using are javax.ws.rs.core.Response and javax.ws.rs.core.MediaType.
Am I understanding the functionality wrong? Thanks!
I'm trying to implement a Service that automatically starts a download with the requested file.
This is my AJAX call:
function downloadFile(fileName) {
$.ajax({
url : SERVICE_URI + "files/" + fileName,
contentType : 'application/json',
type : 'GET',
success : function (data)
{
alert("done!");
},
error: function (error) {
console.log(error);
}
});
}
and this is my Spring Service method GET:
#RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
public void getFile(#PathVariable("file_name") String fileName,
HttpServletResponse response) {
try {
// get your file as InputStream
FileInputStream fis = new FileInputStream( fileName + ".csv" );
InputStream is = fis;
// copy it to response's OutputStream
ByteStreams.copy(is, response.getOutputStream());
response.setContentType("text/csv");
response.flushBuffer();
} catch (IOException ex) {
throw new RuntimeException("IOError writing file to output stream");
}
}
When my client requests the existing file from the server, the AJAX success() method is executed but the file is not even downloading. Am I doing anything wrong?
Don't use ajax, just set window.location.href to the url of the file and set the http content disposition header in your server script to force the browser to save the file.
function downloadFile(fileName) {
window.location.href = SERVICE_URI + "files/" + fileName;
}