Save response as file - javascript

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

Related

download a PDF with Javascript/Node.Js after generating data and storing the file into a path

I created a generator for PDF files that creates the file and sends to a specific path automatically after creation. Still I want to download it right after but didn't know how to do that. Any help would be appreciated. This is my generatorPdf.js :
module.exports={
pdfGenerator:function(data,pathfile){
var fonts = {
Roboto: {
normal: 'server/pdfgenerator/fonts/Roboto-Regular.ttf',
bold: 'server/pdfgenerator/fonts/Roboto-Medium.ttf',
italics: 'server/pdfgenerator/fonts/Roboto-Italic.ttf',
bolditalics: 'server/pdfgenerator/fonts/Roboto-MediumItalic.ttf'
}
};
var datePaiements='';
var dateFinPaiements='';
if(data.abonnement[0].datePaiement!=null)
datePaiements= new Date( data.abonnement[0].datePaiement.toString());
if(datePaiements!=='')
{
dateFinPaiements= ('0'+datePaiements.getDate()).slice(-2).toString()+'/'+('0'+(datePaiements.getMonth()+1)).slice(-2).toString()+'/'+(datePaiements.getFullYear()+1).toString();
datePaiements=('0'+datePaiements.getDate()).slice(-2).toString()+'/'+('0'+(datePaiements.getMonth()+1)).slice(-2).toString()+'/'+datePaiements.getFullYear().toString();
}
var dateFacture= new Date(data.abonnement[0].timestampCreation.toString());
dateFacture= ('0'+dateFacture.getDate()).slice(-2).toString()+'/'+('0'+(dateFacture.getMonth()+1)).slice(-2).toString()+'/'+dateFacture.getFullYear().toString();
var PdfPrinter = require('pdfmake/src/printer');
var printer = new PdfPrinter(fonts);
var fs = require('fs');
var dd = {
content: [ ..............],
footer:{.............}
}
try{
var pdfDoc = printer.createPdfKitDocument(dd);
if (fs.existsSync(pathfile)) {//server/pdfgenerator/documentpdf/basics21.pdf
fs.unlink(pathfile, (err) => {//server/pdfgenerator/documentpdf/basics21.pdf
if (err) {
console.error(err)
return
}
})
}
pdfDoc.pipe(fs.createWriteStream(pathfile)).on('finish',function(){//server/pdfgenerator/documentpdf/basics21.pdf
});
}
catch(e){
console.log(e);
return null;
}
}
}
and this is my remote method in Loopback to send the pdf to a path and where probably I have to do the download of the file:
cm_abonnements.getAbonnementById= async (options,req,res)=>{
const token = options && options.accessToken;
const userId = token && token.userId;
try{
if(userId!==null){
let dataComedien= await app.models.cm_comediens.getComedienByUser(userId);
let argAbn={};
const form = new formidable.IncomingForm();
var formPromise = await new Promise(function(resolve,reject){
form.parse(req,function(err,fields,files){
if(err)
{
reject(err);
return-1
}
console.log(fields.key)
argAbn.idAbonnement=fields.key;
resolve();
})
})
let dataFac=await cm_abonnements.find({where :{and :[{idAbonnement:argAbn.idAbonnement},{idComedien : dataComedien.idComedien}]}});
var data={abonnement:[]};
data.abonnement=dataFac;
var str_date= new Date(dataFac[0].timestampCreation.toString());
var nameFile= 'Fac_'+dataFac[0].idFacture+'_'+str_date.getFullYear().toString()+'-'+('0'+str_date.getMonth()+1).slice(-2).toString()+'-'+('0'+str_date.getDate()).slice(-2).toString()+'.pdf';
var path='public/upload/Comediens/'+dataComedien.idComedien.toString()+'/factures/'+nameFile;
createPdf.pdfGenerator(data,path);
return dataFac;
}
return null;
}
catch(e){
console.log(e);
return null;
}
}
cm_abonnements.remoteMethod(
'getAbonnementById',{
http:{
verb:'POST'
},
description:'Get detail facture by number facture',
accepts:[
{arg:"options", "type":"object","http":"optionsFromRequest"},
{ arg: 'req', type: 'object', 'http': {source: 'req'}},
{arg: 'res', type: 'object', 'http': {source: 'res'}}
],
returns:{arg:'data',root:true}
}
);
Any help would be appreciated. Thank you
You need to send the following HTTP headers:
Content-Type: application/pdf
Content-Disposition: inline; filename="download.pdf"
After data is generated and pdf file is stored, there are 2 steps left to implement the "download" feature:
Return HTTP response to browser, with Content-Type header as application/pdf, and Content-Disposition header as attachment; filename="yourname.pdf". Normally, this would be handled automatically by web framework. I'm not familiar with loopback, so take Express for example:
In generatorPdf.js, add a callback to listen the finish event:
pdfGenerator:function(data, pathfile, callback){
...
pdfDoc.pipe(fs.createWriteStream(pathfile)).on('finish', callback);
...
}
When pdfGenerator function is used, pass a callback function parameter. If the pdf work is "finished", return response to browser using res.download() (It's Express API, but I believe loopback has similar API as loopback is built on top of Express):
var nameFile=...
var path=...
createPdf.pdfGenerator(data, path, function() {
res.download(path, nameFile);
});
In browser side, if it's an AJAX request (I guess so, as you mentioned it is a POST request), you need to handle the request with some blob operation. Here is an example snippet, with explanation comment:
var req = new XMLHttpRequest();
req.open('POST', '/download', true); // Open an async AJAX request.
req.setRequestHeader('Content-Type', 'application/json'); // Send JSON data
req.responseType = 'blob'; // Define the expected data as blob
req.onreadystatechange = function () {
if (req.readyState === 4) {
if (req.status === 200) { // When data is received successfully
var data = req.response;
var defaultFilename = 'default.pdf';
// Or, you can get filename sent from backend through req.getResponseHeader('Content-Disposition')
if (typeof window.navigator.msSaveBlob === 'function') {
// If it is IE that support download blob directly.
window.navigator.msSaveBlob(data, defaultFilename);
} else {
var blob = data;
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = defaultFilename;
document.body.appendChild(link);
link.click(); // create an <a> element and simulate the click operation.
}
}
}
};
req.send(JSON.stringify({test: 'test'}));

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

Dynamic PDF and opening a new window instead of downloading

I have a web api that is returning a JSReport as an encoded byte array. No matter how i try and read the byte array I either get a black screen or an error message that says "failed to download pdf". If I create a hidden anchor tag and download the pdf it works fine. However, I do not want the user to download it, I would prefer they can view it right from their browser.
WEB API CALL
var data = LossReportService.GetLossSummary(request);
var pdf_bytes = LossReportService.GeneratePDFUsingJSReport(data);
byte[] myBinary = new byte[pdf_bytes.Length];
pdf_bytes.Read(myBinary, 0, (int)pdf_bytes.Length);
string base64EncodedPDF = System.Convert.ToBase64String(myBinary);
var response = Request.CreateResponse(HttpStatusCode.OK, base64EncodedPDF);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
response.Content.Headers.ContentLength = pdf_bytes.Length;
return response;
Javascript
$.ajax({
type: "POST",
url: "/Reporting/GetLossSummary",
data: { dataObj },
},
success: function (data) {
if (data != null) {
//I have tried this
var file = new Blob([data], { type: 'application/pdf;base64' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL, "LossSummaryReport");
//which gives me a "failed to load pdf document" error
//and I have tried this, which just renders a blank page
window.open("data:application/pdf," + encodeURI(data));
}
}
});
Any suggestions would be greatly appreciated.
since you are using jsreport, in a normal case, you can use the jsreport browser sdk to better work with the report result and to easily show it in browser. but in your case, you are using a custom url in your server to render your report, so the jsreport browser sdk can't help you in that case. you need instead to work with the report request and response with either jQuery ajax or plain XMLHttpRequest.
working with blob/binary data is hard to do it with jQuery.ajax, you would need to add a data transport to $.ajax in order to handle binary data
/**
*
* jquery.binarytransport.js
*
* #description. jQuery ajax transport for making binary data type requests.
* #version 1.0
* #author Henry Algus <henryalgus#gmail.com>
*
*/
// use this transport for "binary" data type
$.ajaxTransport("+binary", function(options, originalOptions, jqXHR){
// check for conditions and support for blob / arraybuffer response type
if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob)))))
{
return {
// create new XMLHttpRequest
send: function(headers, callback){
// setup all variables
var xhr = new XMLHttpRequest(),
url = options.url,
type = options.type,
async = options.async || true,
// blob or arraybuffer. Default is blob
dataType = options.responseType || "blob",
data = options.data || null,
username = options.username || null,
password = options.password || null;
xhr.addEventListener('load', function(){
var data = {};
data[options.dataType] = xhr.response;
// make callback and send data
callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
});
xhr.open(type, url, async, username, password);
// setup custom headers
for (var i in headers ) {
xhr.setRequestHeader(i, headers[i] );
}
xhr.responseType = dataType;
xhr.send(data);
},
abort: function(){
jqXHR.abort();
}
};
}
});
but when handling blob data in an request/response i prefer doing it with XHTMLRequest directly because it let me manipulate the response in any way i want.
function sendReportRequest (dataObj, cb) {
var xhr = new XMLHttpRequest()
var data = JSON.stringify(dataObj)
xhr.open('POST', 'http://url-of-your-server/' + '/Reporting/GetLossSummary', true)
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8')
xhr.responseType = 'arraybuffer'
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
var response = xhr.response
var contentType = xhr.getResponseHeader('Content-Type')
var dataView = new DataView(response)
var blob
try {
blob = new Blob([dataView], { type: contentType })
cb(null, blob)
} catch (e) {
if (e.name === 'InvalidStateError') {
var byteArray = new Uint8Array(response)
blob = new Blob([byteArray.buffer], { type: contentType })
cb(null, blob)
} else {
cb(new Error('Can not parse buffer response'))
}
}
} else {
var error = new Error('request failed')
error.status = xhr.status
error.statusText = xhr.statusText
cb(error)
}
}
xhr.onerror = function () {
var error = new Error('request failed')
error.status = xhr.status
error.statusText = xhr.statusText
cb(error)
}
xhr.send(data)
}
sendReportRequest(dataObj, function (err, reportBlob) {
if (err) {
return console.error(err)
}
var reportFileUrl = URL.createObjectURL(reportBlob)
window.open(reportFileUrl)
})
with this piece of code you should be able to request a pdf file and show it right in the browser in a new window

AngularJS: $http.get method to download the pdf file

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

using zip.js to read a zip file via xmlhttp/ajax call on Node.js

I am trying to :
Send a zip file via xmlhttp to the client
then read the file using zip.js and render its contents
I successfully receive the binary of the file i.e. the success callback is called but I get and error when I try to do getEntries. I think the error is with the way of sending stream , please help.
Error msg :
Error in reading zip file
My client side code (using angular) :
$http.get(window.location.origin + '/book/'+bookName,{responseType:"Blob"}).
success(function (data , error) {
var a = new Uint8Array(data);
//var dataView = new DataView(data);
//var blob = new Blob(dataView.buffer);
zip.useWebWorkers = true;
zip.workerScriptsPath = '/js/app/';
zip.createReader(new zip.BlobReader(data), function(reader) {
// get all entries from the zip
reader.getEntries(function(entries) { //HERE I GET THE ERROR
if (entries.length) {
// get first entry content as text
entries[0].getData(new zip.TextWriter(), function(text) {
// text contains the entry data as a String
console.log(text);
// close the zip reader
reader.close(function() {
// onclose callback
var a = 0;
});
}, function(current, total) {
// onprogress callback
var a = 0;
});
}
});
},
function(error) {
// onerror callback
var a = 0;
});
})
.error( function (data , error) {
var a = 0;
});
My Server side code on Node:
router.get('/book/:bookName',function (req , res ) {
console.log('Inside book reading block : ' + req.params.bookName);
req.params.bookName += '.zip';
var filePath = path.join(__dirname,'/../\\public\\books\\' ,req.params.bookName );
var stat = fileSystem.statSync(filePath);
res.writeHead(200, {
//'Content-Type': 'application/zip',
'Content-Type': 'blob',
'Content-Length': stat.size
});
var readStream = fileSystem.createReadStream(filePath);
// replace all the event handlers with a simple call to readStream.pipe()
readStream.pipe(res);
});
It is probable that you might have already found a solution. I faced the same problem today and this is how I solved it in plain javascript:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'assets/object/sample.zip', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
// response is unsigned 8 bit integer
var responseArray = new Uint8Array(this.response);
var blobData = new Blob([responseArray], {
type: 'application/zip'
});
zip.createReader(new zip.BlobReader(blobData), function(zipReader) {
zipReader.getEntries(displayEntries);
}, onerror);
};
xhr.send();
The problem I see in your code is that you are changing the value to Uint8Array and assigning it to var a, but still use the raw data in blobreader. Also the blob reader required blob and not an array. So you should have converted var a into blob and then used it for reading.

Categories