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;
});
}
Related
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}}
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();
}
I am trying to download a pdf file from server and show it in browser. The web api method that I have returns the file as httpResponseMessage and that is working fine because it returns the file. But on the AngularJs side I am not able to display the file. Can somebody help me understand what am I missing?
Web Api Method:
public HttpResponseMessage GetHelpReferenceDocs(Guid streamKey)
{
var fakeFileName = GetStream(streamKey); // If this succeeds, stream is known and unexpired.
// Internal file name
string staticFileName = helpFiles[fakeFileName];
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Static/" + staticFileName);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(mappedPath, FileMode.Open, FileAccess.Read, FileShare.Read);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = Path.GetFileName(mappedPath);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
result.Content.Headers.ContentLength = stream.Length;
return result;
}
AngularJS:
function loadDocument(fileName) {
REST.post(commonService.constants.webapi.helpFileStreamKey + fileName)
.then(function(response) {
var streamGuid = response.data;
REST.get(commonService.constants.webapi.helpReferenceGuide + streamGuid).then(function (response) {
$window.open(response.data);
});
})
.catch(function (e) { $scope.errorHandler(moduleName, e); })
.finally($scope.waitOn);
}
Take a look at the suggestions here, or alternatively you can try an npm module specifically for displaying pdfs where this is already taken care of for you, such as this one.
Ok I found the solution. Needed to modify my Angular code a little bit and it worked fine. Here is the code if somebody facing the same problem:
function loadDocument(fileName) {
REST.post(commonService.constants.webapi.helpFileStreamKey + 'firmUserGuid')
.then(function(response) {
var streamGuid = response.data;
REST.get(commonService.constants.webapi.helpReferenceGuide + streamGuid).then(function (response) {
var pdfFileURL = response.config.url;
$window.open(pdfFileURL);
});
})
.catch(function (e) { $scope.errorHandler(moduleName, e); })
.finally($scope.waitOn);
}
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
});
Seems like a straightforward issue, but how can you retrieve an image stored in a database, using Web API, then display it using Angular?
Here is an example Web API service which is correctly returning a JPG file (using HttpResponseMessage):
public HttpResponseMessage GetIncidentImages(Guid IncidentIDX) {
var response = new HttpResponseMessage();
List<T_EM_INCIDENT_ATTACH> att = db_Layer.GetT_EM_INCIDENT_ATTACH_byIncidentIDX(IncidentIDX);
if (att != null)
{
if (att.Count > 0)
{
var pictureBytes = att[0].ATTACH_CONTENT; //ATTACH_CONTENT is a byte array
if (pictureBytes == null)
response.StatusCode = HttpStatusCode.NotFound;
else
{
response.Content = new ByteArrayContent(pictureBytes);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
}
}
else
response.StatusCode = HttpStatusCode.NotFound;
}
return response;
}
Then on the http client-side, I am using angular to retrieve the data. Data is definitely getting retrieved, but just not displayed.
dbManagerService.syncIncidentAttach(ehConstants.incidenT_IDX).then(function (res) {
console.log("return", res);
$scope.cameraPic = res;
});
function _syncIncidentAttach(incIDX) {
var deferred = $q.defer();
$http.get($rootScope.serverBaseUrl + 'api/incident/GetIncidentImages?IncidentIDX=' + incIDX, { responseType: "blob" })
.success(function (res, status, headers, config) {
// encode data to base 64 url
fr = new FileReader();
fr.onload = function () {
// this variable holds your base64 image data URI (string)
// use readAsBinary() or readAsBinaryString() below to obtain other data types
console.log(fr.result);
};
fr.readAsDataURL(res);
deferred.resolve(fr);
})
.error(function(data, status, headers, config) {
conole.log('error getting image');
});
return deferred.promise;
}
And the html:
<img ng-src="{{cameraPic}}" /> </div>
Looking at your server side code, I think you can directly write like this:
<img ng-src="{{serverBaseUrl}}api/incident/GetIncidentImages?IncidentIDX={{ehConstants.incidenT_IDX}}" />
Just make sure you are replacing ehConstants.incidenT_IDX with actual content.
As documented in this answer, you can also do something like
<img ng-src="{{'data:image/png;base64,' + main.user.imageData}}">