How to download excel file by using Spring response - javascript

I'm trying to download excel file by using Jasper Report 6.2.2
Here is my Spring Controller:
#RequestMapping(value = "/downloadExcel", method = RequestMethod.POST)
#ResponseBody
public void downloadMyReportExcelFile(#RequestBody ExcelFilter excelFilter, HttpServletResponse response) {
try {
reportExportBo.downloadReportFile(response, excelFilter);
} catch (Throwable e) {
LOGGER.error("Unknown error at REST Service", e);
}
}
and also here is my downloadReportFile method codes:
#Override
public void downloadReportFile(HttpServletResponse response, ExcelFilter excelFilter) {
List<myClassObject> myObjectList= objectRecordBo.myData(excelFilter);
InputStream is = this.getClass().getClassLoader().getResourceAsStream("/my_reports.jrxml");
ExcelExporter exporter = new ExcelExporter();
String fileName = "my_exported_report.xls";
JasperDesign jd = JRXmlLoader.load(is);
JasperReport jr = JasperCompileManager.compileReport(jd);
JasperPrint jprint = JasperFillManager.fillReport(jr, null, new JRBeanCollectionDataSource(myObjectList));
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
JRXlsExporter xlsExporter = new JRXlsExporter();
xlsExporter.setExporterInput(new SimpleExporterInput(jprint));
xlsExporter.setExporterOutput(new SimpleOutputStreamExporterOutput(response.getOutputStream()));
SimpleXlsReportConfiguration xlsReportConfiguration = new SimpleXlsReportConfiguration();
xlsReportConfiguration.setOnePagePerSheet(false);
xlsReportConfiguration.setRemoveEmptySpaceBetweenRows(true);
xlsReportConfiguration.setDetectCellType(false);
xlsReportConfiguration.setWhitePageBackground(false);
xlsExporter.setConfiguration(xlsReportConfiguration);
xlsExporter.exportReport();
my_reports.jrxml is suitable for myObjectList, columns and variables are same.
Also here is my javascript function;
function downloadService(url, paramData, fileName, $http) {
return $http.post(url, paramData, {responseType:'Content-Type'}).then(function (response) {
var blob = new Blob([response.data], {type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
var objectUrl = URL.createObjectURL(blob);
var a = document.createElement("a");
a.style = "display: none";
a.href = objectUrl;
a.download = fileName + '.xls' ;
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(objectUrl);
}, 100);
}, function (response) {
//TODO
});
}
After calling downloadService method, i got excel downloaded but it is not readable
What do i wrong?
EDITED:
By the way when i'm using in html side;
<a style="float:right; " href="service/downloadExcel">{{ 'EXPORT_EXCEL' | translate}}</a>
and Spring controller is GET and no any #RequestBody, it works fine. But I need to pass parameters with JSON Object, so i can not use it.

I solved my problem.
First I set Header and Content Type of Response, before JasperDesign.
...
if (list != null && list.size() > 0) {
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
JasperDesign jd = JRXmlLoader.load(reportStream);
JasperReport jr = JasperCompileManager.compileReport(jd);
...
Also I updated my ajax service as;
...
return $http.post(newUrl, paramData, {responseType: 'arraybuffer'}).then(function (response) {
var blob = new Blob([response.data], {type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=UTF-8'});
....
Thanks for helping guys...

Here is the another solution(javascript side) might be helpful for others:
var request = new XMLHttpRequest();
request.open('POST', url, true); // update the url
request.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
request.responseType = 'blob';
var fileName = "simulation_results.xlsx"; // file name
request.onload = function (e) {
if (this.status === 200) {
var blob = this.response;
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, fileName);
} else {
var downloadLink = window.document.createElement('a');
var contentTypeHeader = request.getResponseHeader("Content-Type");
downloadLink.href = window.URL.createObjectURL(new Blob([blob], {
type: contentTypeHeader
}));
downloadLink.download = fileName;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
}
};
request.send(JSON.stringify(data)); // request data

Related

How do I return a byte array as response using spring boot and ajax?

I am trying to create a page that will download a file from a data stream. I am able to get the byte stream and I want to return this to client side to save it as file.
I tried the code below however, the file that is being saved is corrupted. The download process also is too quick that I think it is not downloading properly..
Java code:
#RequestMapping(value = /download, method=RequestMethod.POST)
public ResponseEntity<ByteArrayResource> download() {
byte b[] = <retrieving byte data array here>;
ByteArrayResource resource = new ByteArrayResource(b);
ResponseEntity<ByteArrayResource> r = ResponseEntity.ok()
.header("Content-type", "application/octet-stream")
.header("Content-disposition", "attachment; filename=\"test.mp4\"")
.contentLength(resource.contentLength())
.body(resource);
return r;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
Javascript code:
$('#download').click(function() {
$.ajax({
type: "POST",
url: "/download",
data: {}
}).done(function(data) {
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
var blob = new Blob([data], {type: "octet/stream"});
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = "test.mp4";
a.click();
}
}).fail(function(data) {
});
});
Hope you guys could help me.

How can download every type file in ajax?

I need to download every file of type js and c#.
This is my api code:
[HttpPost]
public HttpResponseMessage GetFile(DownloadInput input)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
var fileNm = input.FileName;
string filePath = (#"C:\Uploads\" + input.ID + #"\" + fileNm);
if (!File.Exists(filePath))
{
response.StatusCode = HttpStatusCode.NotFound;
response.ReasonPhrase = string.Format("File not found: {0} .", fileNm);
throw new HttpResponseException(response);
}
byte[] bytes = File.ReadAllBytes(filePath);
response.Content = new ByteArrayContent(bytes);
response.Content.Headers.ContentLength = bytes.LongLength;
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileNm;
response.Content.Headers.ContentType = new MediaTypeHeaderValue(MimeMapping.GetMimeMapping(filePath));
return response;
}
I think ı am rong this part this code download every file but just work .txt type file, I think blob type is false but ı am new this subject ı am tried every code ,
This is my js code:
function FileDown(response, name) {
let blob = new Blob([response], { type: "application/octet-stream" });
let link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = name;
link.click();
}
You can try the following, I've used it successfully many times.
First you need to make sure the data you save is correct. Since you need the mimetype to successfully download files without complications.
Here is the C# (PS: You can make your Endpoint HttpGet, instead of HttpPost.)
[HttpGet]
[Route("YourController/{fileName}")]
public HttpResponseMessage Download(string fileName) //Parameter is yours
{
string filePath = (#"C:\Uploads\" + input.ID + #"\" + fileName);
if (!File.Exists(filePath))
{
response.StatusCode = HttpStatusCode.NotFound;
response.ReasonPhrase = string.Format("File not found: {0} .", fileNm);
throw new HttpResponseException(response);
}
byte[] bytes = File.ReadAllBytes(filePath);
using (var ms = new MemoryStream(bytes))
{
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(ms.ToArray())
};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName //String value of the file name.
};
string mimeType = MimeMapping.GetMimeMapping(fileName); //I've found that this does not always work. Go here for a better answer: https://stackoverflow.com/questions/1029740/get-mime-type-from-filename-extension
result.Content.Headers.ContentType = new MediaTypeHeaderValue(mimeType); //The mime type retrieved from the
return result;
}
}
In javascript you can use: window.open('yourApiURL/YourController/FileName');

XLSX client save from backend api response: binary string, application/octet-stream

I need to save XLSX file on frontend that im getting from XHR to api.
In api response i got this headers:
Content-Type: application/octet-stream
content-disposition: attachment; filename = OrdersList-253CREATED0.xlsx
And part of response body:
PKõzMdocProps/core.xml­MKÄ0ïý!÷vVd-mQÅ++ÞB:¶Åæ$Úõßí®Å£ÇÉû¼ä\ïåHÞѺA«²$¥Ðí º>6xE×uB[ÜZmÐú -å*Ú{o
'zÜ%!V!yÑVrFÛáâwYDÏ[î9l±Ytôè+ùwe+¥y³ã,hàwÀ߬G+Ý9YȽj¦dÊg.lÄàéîöa^>ó\ ¤uDHy²Â"÷Øà(üÁ~%»üêºÙÐ:KÙ*fYÌ.,-²¼8ËKøÕ?9£¶õe8Kd{ ...
In my code, I tried many options:
1) replace type: "application/octet-stream" to application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
2) creating blob/file without s2ab function like this Blob([binary_string], ...)
let binary_string = response;
// Download using blob:
let ab = s2ab(binary_string),
blob = new Blob(
[ab],
{type: "application/octet-stream"}
);
let downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = 'test-blob.xlsx';
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
// Download using file
let FileSaver = require('file-saver'),
ab = s2ab(binary_string),
file = new File(
[ab],
"test-file.xlsx",
{type: "application/octet-stream"});
FileSaver.saveAs(file);
// s2ab function
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
Why i cant just use iframe, or a[download]? Because authorization header is required.
The file from response is correct, it opens when loading via postman, but when im trying to save it via js from XHR response it always corrupted.
I would be very grateful for the help :)
Im solved the problem.
In this projects im using react, so ofc im used axios for ajax requests.
I think the problem in some axios settings or with axios itself;
Working code example on vanilla js:
var request = new XMLHttpRequest();
request.open('GET', `http://localhost/api/get_xlsx`, true);
request.setRequestHeader('Token', 'user_auth_token_needed_in_my_app');
request.responseType = 'blob';
request.onload = function(e) {
if (this.status === 200) {
var blob = this.response;
if(window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, 'test.xlsx');
}
else{
var downloadLink = window.document.createElement('a');
var contentTypeHeader = request.getResponseHeader("Content-Type");
downloadLink.href = window.URL.createObjectURL(new Blob([blob], {
type: contentTypeHeader }));
downloadLink.download = 'test.xlsx';
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
}
};
request.send();

Why the file cannot be save properly? [duplicate]

download any file using ResponseEntity with angular does not work
I need to download a file using angular in client side,
this file can have any format it could be a pdf or excel or image or txt ...
my method works just for txt files and gives me a fail format for excel and image and for the pdf it gives an empty pdf.
so in my controller here is the function that calles the service method:
vm.downloadFile = downloadFile;
function downloadFile(file){
var urlDir = "C://STCI//"+idpeticion;
return VerDocServices.downloadFile(file,urlDir)
.then(function(response) {
var data = response.data;
var filename = file;
var contentType = 'application/octet-stream';//octet-stream
var linkElement = document.createElement('a');
try {
var blob = new Blob([ data ], {
type : contentType
});
var url = window.URL.createObjectURL(blob);
linkElement.setAttribute('href', url);
linkElement.setAttribute("download", filename);
var clickEvent = new MouseEvent("click", {
"view" : window,
"bubbles" : true,
"cancelable" : false
});
linkElement.dispatchEvent(clickEvent);
} catch (ex) {
console.log(ex);
throw ex;
}
}).catch(function(response) {
alert('Se ha producido un error al exportar del documento');
console.log(response.status);
throw response;
});
}
and my service.js has:
angular.module('mecenzApp').service('VerDocServices',['$http',function($http) {
this.downloadFile = function(file,urlDir) {
return $http.get('api/downloadFile', {
params : {
file : file,
urlDir : urlDir
}
}); }} ]);
And my service method is this:
#GetMapping("/downloadFile")
#Timed
public ResponseEntity<byte[]> downloadFile(#RequestParam(value = "file") String file, #RequestParam(value = "urlDir") String urlDir) {
log.debug("GET ---------------- DOWNLOAD FILE : {}", file);
log.debug("GET ---------------- From the DIRECTORY: {}",urlDir);
InputStream fileStream;
String filepath = urlDir+File.separator+file;
try {
File f = new File(filepath);
log.debug("GET ---------------- FILE: {}",f.getPath());
fileStream = new FileInputStream(f);
byte[] contents = IOUtils.toByteArray(fileStream);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/octet-stream"));
String filename = file;
headers.setContentDispositionFormData(filename, filename);
ResponseEntity<byte[]> response2 = new ResponseEntity<byte[]>(contents, headers, HttpStatus.OK);
fileStream.close();
return response2;
} catch (FileNotFoundException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
return null;
}
could you plz take a look and tell me what did I have missed??
Thank youuu :)
How to Download Binary Files with AngularJS
When downloading binary files, it is important to set the responseType:
app.service('VerDocServices',['$http',function($http) {
this.downloadFile = function(url, file, urlDir) {
var config = {
//SET responseType
responseType: 'blob',
params : {
file : file,
urlDir : urlDir
}
};
return $http.get(url, config)
.then(function(response) {
return response.data;
}).catch(function(response) {
console.log("ERROR: ", response.status);
throw response;
});
};
}]);
If the responseType is omitted the XHR API defaults to converting UTF-8 encoded text to DOMString (UTF-16) which will corrupt PDF, image, and other binary files.
For more information, see MDN Web API Reference - XHR ResponseType
I don't know much about the backend, but I'll provide what i have used may be it will help, so On the Java Script File:
//your $http(request...)
.success(function (data, status, headers, config) {
//Recieves base64 String data
var fileName = 'My Awesome File Name'+'.'+'pdf';
//Parsing base64 String...
var binaryString = window.atob(data);
var binaryLen = binaryString.length;
var fileContent = new Uint8Array(binaryLen);
for (var i = 0; i < binaryLen; i++) {
var ascii = binaryString.charCodeAt(i);
fileContent[i] = ascii;
}
var blob = new Blob([fileContent], { type: 'application/octet-stream' }); //octet-stream
var fileURL = window.URL.createObjectURL(blob);
$sce.trustAsResourceUrl(fileURL); //allow angular to trust this url
//Creating the anchor download link
var anchor = angular.element('<a/>');
anchor.css({display: 'none'}); // Make sure it's not visible
angular.element(document.body).append(anchor); // Attach it to the document
anchor.attr({
href: fileURL,
target: '_blank',
download: fileName
})[0].click();
anchor.remove(); // Clean it up afterwards
})
//.error(function(...
And On your backend, make sure that your webservice produces octet-stream and returning the file in base64 data format, i did this using Java JAX-RS like this:
#POST
#Path("/downloadfile")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response downloadFile(...){
String base64String = Base64.getEncoder().encodeToString(/*here you pass your file in byte[] format*/);
return Response.ok(base64String).build();
}

Handling stream from server and displaying the file in browser

I'm trying to find the global solution for handling strem files from the server.
My response object is angular http Response
My object has differnet _body for images/docs etc.
images is a long string of ##$.. and docs are json
I've been looking for solutions over the web, and got to implement this:
let contentType = resData.headers.get("Content-Type");
let blob = new Blob([resData.arrayBuffer()], {type: contentType});
let url = window.URL.createObjectURL(blob);
window.open('url: ', url);
This code downloads a file that has content-type of octet-stream
but all other files are not displayed in the browser.
My main goal is to have the same behavior if would have put in the URL the API that returns a stream, and the browser knows how to handle it (images are shown in browser, files that browser doesn't support are automatically downloaded etc.)
This is the code for the request.
return Observable.create(observer => {
let xhr = new XMLHttpRequest();
xhr.open(Object.keys(RequestMethod).find(k => RequestMethod[k] === url.method), url.url, true);
const shift =
xhr.setRequestHeader('Content-type', 'application/json');
xhr.responseType = (Object.keys(ResponseContentType).find(k => ResponseContentType[k] === url.responseType)).toLocaleLowerCase();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let blob = new Blob([xhr.response], {type: body.MimeType});
observer.next({blob: blob, fileName: body.FileName});
observer.complete();
} else {
observer.error(xhr.response);
}
}
}
xhr.send(url.getBody());
This is the code for the special handling of each mimeType
handleAttachmentItem(resData) {
let blob = resData.blob;
const fileName = resData.fileName;
if (blob.type.includes('image')) {
let b64Response = window.URL.createObjectURL(blob);
let outputImg = document.createElement('img');
outputImg.src = b64Response;
let w = window.open();
w.document.write('<html><head><title>Preview</title><body style="background: #0e0e0e">');
w.document.write(outputImg.outerHTML);
} else if (blob.type.includes('text')) {
let url = window.URL.createObjectURL(blob);
window.open(url);
} else {
let a = document.createElement("a");
document.body.appendChild(a);
let url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
}
}

Categories