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();
}
Related
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.
I have an ASP.Net Web API method which is returning a zip file, which seems happening properly, now what I want is, I want to download that zip file at the client location using React. My Api method is as follows:
[HttpPost]
public FileContentResult Post([FromForm] string communityName, [FromForm] string files)
{
var removedInvalidCharsFromFileName = removeInvalidCharsFromFileName(files);
var tFiles = removedInvalidCharsFromFileName.Split(',');
string rootPath = Configuration.GetValue<string>("ROOT_PATH");
string communityPath = rootPath + "\\" + communityName;
byte[] theZipFile = null;
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
foreach (string attachment in tFiles)
{
var zipEntry = zip.CreateEntry(attachment);
using (FileStream fileStream = new FileStream(communityPath + "\\" + attachment, FileMode.Open))
using (Stream entryStream = zipEntry.Open())
{
fileStream.CopyTo(entryStream);
}
}
}
theZipFile = zipStream.ToArray();
}
return File(theZipFile, "application/zip", communityName + ".zip");
}
And my React/JS method is as follows:
handleDownload = (e) => {
e.preventDefault();
var formData = new FormData();
formData.append('communityname', this.state.selectedCommunity);
formData.append('files', JSON.stringify(this.state['checkedFiles']));
let env = 'filesApi.' + clientConfiguration['Environment'];
let url = clientConfiguration['filesApi.local'];
alert(url);
axios({
method: 'post',
url: url,
data: formData
})
.then(res => {
//console.log(res.data);
var binaryData = [];
binaryData.push(res.data);
const src = window.URL.createObjectURL(new Blob(binaryData, { type: "application/zip" }));
var fileName = `${this.state['selectedCommunity']}.zip`;
saveAs(src, fileName);
});
};
It is downloading the zip file, but says the following error when I try to unzip the file at the client location, what am I missing - not able to understand:
Can not open the file as zip archive, is not archiving, Warning Headers error
, any help please
I'm working in a solution with SOA architect, and I have got an issue converting a .zip file in a byte array on WS Layer and download by a web API from the presentation layer.
The zip file download is successful, but it's not possible to unzip the file.
Let me explain with code:
Business Layer
On business layer we've defined a method that converts a file zip on a byte array
//This method is defined on business layer and exposed on WS in WCF Layer
//Class: BusinessLayer
public byte[] convertingZip(){
try{
pathFile = "directoryOnServer/myZipFile.zip"
byte[] arr = File.ReadAllBytes(pathFile);
return arr;
}catch(Exception ex){ /*Do something*/ }
}
WCF Services Layer
On WCF services layer, we code a method that returns the array bytes and exposed it
//Class: ServiceLayer
public byte[] getByteArray(){
try{
BusinessLayer blObject = new BusinessLayer();
return blObject.convertingZip();
}catch(Exception ex){ /*Do something*/ }
}
Web API
On Web API project, we code a method that consumes the WCF service layer and return byte array into content
//This controller must be return the zip file
[HttpGet]
[AuthorizeWebApi]
[Route("downloadZip")]
public async Task<IHttpActionResult> downloadZipFile(){
try{
using(ServiceLayer services = new ServiceLayer()){
arr = services.getByteArray();
var result = new HttpResponseMensage(HttpStatusCode.OK){
Content = new ByteArrayContent(arr); }
result.Content.Headers.ContentDisposition
= new ContentDispostionHeaderValue("attachment"){
FileName = "zip-dowload.zip" };
result.Content.Headers.ContentType
= new MediaTypeHeaderValue("application/octec-stream");
var response = ResponseMessage(result);
return result;
}
}cacth(Exception ex){ /*Do something*/ }
}
Presentation Layer
On presentation layer I download the file with angular JS 1.6.5
//On Web App project consume the WebApi with Angular
//MyController.js
$scope.DonwloadZip = function(){
$http.get('api/myControllerUrlBase/downloadZip')
.success(function(data, status, headers, config){
if(status === true && data != null){
var file = new Blob([data], {type: "application/zip"});
var fileURL = URL.createObjectUrl(file);
var a = document.createElement(a);
a.href = fileURL;
a.target = "_blank";
a.download = "MyZipFileName.zip";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}else { /*Do something */}
})
.error(function(data, status, headers, config) {
//show error message
});
}
I'm not sure that doing right. I test some similar with .xml, .txt. and .csv files and works. But don't work with zip files.
Then, What is the correct way to convert a zip file in a byte array and getting my web API from web app project?
I'll very grateful for help.
Well, I've resolve the issue and I would like share the solution.
The central problem it's on web-api and presentation layer, then we need modify the code here:
On Api Controller, convert the byte array into a string on base64 and send on http response content
[HttpGet]
[AuthorizeWebApi]
[Route("downloadZip")]
public async Task<IHttpActionResult> downloadZipFile(){
try{
//Using WS reference
using(ServiceLayer services = new ServiceLayer()){
//Catch the byte array
arr = services.getByteArray();
//Encode in base64 string
string base64String = System.Convert.ToBase64String(arr, 0, arr.length);
//Build the http response
var result = new HttpResponseMensage(HttpStatusCode.OK){
Content = new StringContent(base64String); }
result.Content.Headers.ContentDisposition
= new ContentDispostionHeaderValue("attachment"){
FileName = "zip-dowload.zip" };
result.Content.Headers.ContentType
= new MediaTypeHeaderValue("application/octec-stream");
var response = ResponseMessage(result);
return result;
}
}cacth(Exception ex){ /*Do something*/ }
}
On Angular Controller, no convert data response on Blob, define metadata about data response and download:
$scope.DonwloadZip = function(){
$http.get('api/myControllerUrlBase/downloadZip')
.success(function(data, status, headers, config){
if(status === true && data != null){
//No convert data on Blob
var fileURL = 'data:application/octec-stream;charset=utf-8;base64,'+ data;
var a = document.createElement(a);
a.href = fileURL;
a.target = "_blank";
a.download = "MyZipFileName.zip";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}else { /*Do something */}
})
.error(function(data, status, headers, config) {
//show error message
});
}
Remember that if you're using a Angular 1.6.5 or up version, http request must be something like:
$http({
method: 'GET',
url: 'api/myControllerUrlBase/downloadZip'
}).then(function (response){
//Success
},function (error){
//Error
});
I hope that be useful for somebody, thanks for try help!
I can achieved this from below method
downloadFile(base64: string,filename: string, mimetype: string) {
var fileURL = 'data:application/octec-stream;charset=utf-8;base64,' + base64;
var a = document.createElement('a');
a.href = fileURL;
a.target = "_blank";
a.download = filename + ".pdf";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
<a [href]="fileUrl" (click)="downloadFile(type.base64String,type.name,type.extension)"> {{type.name}}
I have WebAPI method which returns HttpResponseMessage with .csv file as Content:
private static HttpResponseMessage FileAsAttachment(string file)
{
var now = DateTime.Now;
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StringContent(file);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); //attachment will force download
result.Content.Headers.ContentDisposition.FileName = string.Format("Report-{0}.csv", now.ToString("MMMM"));
return result;
}
So I have just click function, which make call to server :
$scope.GenerateReport = function() {
var endDate = '2016-04-30';
UserDaysSummary.generateReport({endDate: endDate }, function (result) {
console.log("Export");
});
}
But all that I've got - is a response with data inside.
I've tried to get it as file using this and this answer, but this doesn't change anything.
Preferably, that call to the server has GET method, btw
Is your GenerateReport function returning a promise? Try this:
userDaysSummary.generateReport = function(endDate) {
var defer = $q.defer();
$http.get('path/to/api', { endDate: endDate }, { responseType: 'arrayBuffer' }).then(function(data, status, headers, config) {
var results = {
data: data, //your file is here
headers: headers(), //headers are here
status: status,
config: config
};
//return a success promise containing the response object
defer.resolve(results);
}, function(data, status, headers, config) {
defer.reject(data);
});
return defer.promise;
}
Then, using the promise to download the file:
userDaysSummary.generateReport(endDate).then(function(response) {
//get the file
var octetStreamMime = 'application/octet-stream';
//get the headers' content disposition
var cd = response.headers["content-disposition"];
//get the file name with regex
var regex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var match = regex.exec(cd);
//is there a fiel name?
var fileName = match[1] || "myDefaultFileName.csv";
//replace leading and trailing slashes that C# added to your file name
fileName = fileName.replace(/\"/g, "");
//determine the content type from the header or default to octect stream
var contentType = response.headers["content-type"] || octetStreamMime;
//finally, download it
try {
var blob = new Blob([response.data], {type: contentType});
//downloading the file depends on the browser
//IE handles it differently than chrome/webkit
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
}
} catch (exc) {
console.log("Save Blob method failed with the following exception.");
console.log(exc);
}
}, function(error) {
//an error occurred while trying the API, handle this
});
I'm using the $http.get(...) method in SPA to get pdf download.
In the SPA ,print method give the blank pdf .
But when i did the debug, data come from the API.
Can you help on this?
This is the API implementation to response the stream out as application/pdf
public Stream GetConsumerInformationReport(Guid id)
{
..........
var stream = cryRpt.ExportToStream(ExportFormatType.PortableDocFormat);
return stream;
}
SPA implementation of get the data from API
var print = function () {
var downloadPath = apiEndPoint + 'Reports/' + $state.current.data.printPrefix + '.pdf';
$http.get(downloadPath,httpConfig).
success(function (data) {
var blob = new Blob([data], { type: "application/pdf" });
var objectUrl = URL.createObjectURL(blob);
$window.open(objectUrl);
}).
error(function (data, status, headers, config) {
// if there's an error you should see it here
});
};
Use FileSaver.js from here http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js
Then define your download method like this. Take it only as an inspiration, do not copy-paste-run :)
The original version can be found here - http://davidjs.com/2015/07/download-files-via-post-request-in-angularjs/
//Define method download() in your ng controller
$scope.download = () => {
//Indicates that download is in progress
$scope.isDownloading = true;
return $http.get(downloadPath,httpConfig).$promise.then((data: any) => {
//using saveAs.js (part of upcoming HTML5 API, but so far a polyfill)
var blob = data.response.blob;
var fileName: string = data.response.fileName || 'document.pdf';
//SaveAs is available at saveAs.js from http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js
(<any>$window).saveAs(blob, fileName);
})
.finally(() => {
$scope.isDownloading = false;
});
}