Download excel file in angular 7 - excel

Hey after struggling to download the excel from server below is the solution i found very easy.But API side they will just read the path and send the files.
How can i differentiate the file type?
If server files are in your project directory or server , we would like to down load the excel or any file directly. Added the below implementation which works only for excel.
API(.net):
public ActionResult Download()
{
string fileName = WebConfigurationManager.AppSettings["filename"];
var filePath = System.Web.HttpContext.Current.Server.MapPath("~/" + fileName);
if (System.IO.File.Exists(filePath))
{
byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
else
{
var response = new WebServiceStatus()
{
code = -1,
data = null,
message = "File is Not available"
};
var data = JsonConvert.SerializeObject(response);
return HandleTrivialHttpRequests(data);
}
}
Angular V7
//Declaration
headers: HttpHeaders;
options: any;
//Constructor or u can have for specific method
this.headers = new HttpHeaders({ 'Content-Type': 'application/json' });
this.options = {
observe: 'response',
headers: this.headers,
responseType: 'arraybuffer'
};
//Service request:
this.httpClient.post('http://localhost:8080/api/report', this.data,
this.option)
.pipe(
catchError(err => this.handleError(err))
).subscribe(response => {
Helper.exportExelFile(response, 'FileName');
});
//In component or in helper function in one class, I have used helper
function which can be reused in other places also
import * as FileSaver from 'file-saver';
function exportExelFile(data, filename) {
const blobValue = new Blob([data['body']], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
FileSaver.saveAs(blobValue, filename + '.' + FileType.EXCEL);
}
export const Helper = {
exportExelFile
};

Related

Axios Excel file download using POST results in corrupted file

I was using Axios to download a file provided by a GET endpoint previously. The endpoint has changed and is now a POST, however the parameters are not required. I'm updating the original download method, but am getting a corrupted file returned.
downloadTemplate() {
axios.post(DOWNLOAD_TEMPLATE_URL,
{
responseType: 'blob',
headers: {
'Content-Disposition': "attachment; filename=template.xlsx",
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
}
})
.then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'template.xlsx');
document.body.appendChild(link);
link.click();
})
.catch((error) => console.log(error));
}
I'm not sure if the issue is with the responseType, headers, or how the response is handled or all of the above. I've tried various options with no luck so far. Any suggestions would be greatly appreciated!
I have been able to download the file using Postman so I know the file served by the endpoint is fine. I just can't sort out the params to do this in my React code.
Finally got it working! The post syntax in the code block for the question was not correct and also changed the responseType to "arraybuffer".
Working example below:
downloadTemplate() {
axios.post(DOWNLOAD_TEMPLATE_URL, null,
{
headers:
{
'Content-Disposition': "attachment; filename=template.xlsx",
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
},
responseType: 'arraybuffer',
}
).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'template.xlsx');
document.body.appendChild(link);
link.click();
})
.catch((error) => console.log(error));
}
We can use the following code to export Excel files from the POST method. May it help someone and save time.
For API use .Net Core 2.2 and the method is below.
Note: When we create a FileStreamResult, Content-Disposition header for the response will contain the filename and the stream will come as an attachment.
Add the "Content-Disposition" to Cors at Startup file,
app.UseCors(b => b.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials().WithExposedHeaders("Content-Disposition"));
I am using the EPplus package for generating the Excel file.
using OfficeOpenXml;
using OfficeOpenXml.Style;
public static MemoryStream InvoiceToExcel(List<InvoiceSearchDto> invoices)
{
var listOfFieldNames = typeof(InvoiceSearchDto).GetProperties().Select(f => f.Name).ToList();
int cellCounter = 1, recordIndex = 2;
var ms = new MemoryStream();
using (ExcelPackage package = new ExcelPackage(ms))
{
ExcelWorksheet worksheet;
worksheet = package.Workbook.Worksheets.Add("New HGS");
// Setting the properties of the first row
worksheet.Row(1).Height = 20;
worksheet.Row(1).Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
worksheet.Row(1).Style.Font.Bold = true;
// Header of the Excel sheet
foreach (string header in listOfFieldNames)
{
worksheet.Cells[1, cellCounter++].Value = header;
}
// Inserting the article data into excel
// sheet by using the for each loop
// As we have values to the first row
// we will start with second row
foreach (InvoiceSearchDto invoice in invoices)
{
worksheet.Cells[recordIndex, 1].Value = invoice.CompanyName;
worksheet.Cells[recordIndex, 2].Value = invoice.CustomerNo;
worksheet.Cells[recordIndex, 3].Value = invoice.DocumentNumber;
worksheet.Cells[recordIndex, 4].Value = invoice.BillingPeriodStartDate.ToString("YYYY-MM-DD");
worksheet.Cells[recordIndex, 5].Value = invoice.BillingPeriodEndDate.ToString("YYYY-MM-DD");
worksheet.Cells[recordIndex, 6].Value = invoice.DateOfInvoice.ToString("YYYY-MM-DD");
worksheet.Cells[recordIndex, 7].Value = invoice.ExpirationDate.ToString("YYYY-MM-DD");
worksheet.Cells[recordIndex, 8].Value = invoice.Amount;
worksheet.Cells[recordIndex, 9].Value = invoice.InvoiceStatusText;
recordIndex++;
}
// By default, the column width is not
// set to auto fit for the content
// of the range, so we are using
// AutoFit() method here.
worksheet.Column(1).AutoFit();
worksheet.Column(2).AutoFit();
worksheet.Column(3).AutoFit();
worksheet.Column(4).AutoFit();
worksheet.Column(5).AutoFit();
worksheet.Column(6).AutoFit();
worksheet.Column(7).AutoFit();
worksheet.Column(8).AutoFit();
worksheet.Column(9).AutoFit();
package.Save();
}
ms.Position = 0;
return ms;
}
The Action Method code is as below
[HttpPost]
[Route("[action]")]
public IActionResult GetInvoiceWithExcel([FromBody]SearchInvoice searchInvoice)
{
try
{
if (!string.IsNullOrEmpty(searchInvoice.InvoiceDateFrom))
{
searchInvoice.DateFrom = Convert.ToDateTime(searchInvoice.InvoiceDateFrom);
}
if (!string.IsNullOrEmpty(searchInvoice.InvoiceDateTo))
{
searchInvoice.DateTo = Convert.ToDateTime(searchInvoice.InvoiceDateTo);
}
var invoices = invoiceBatchService.GetAllForExcel(searchInvoice.PagingParams, searchInvoice, searchInvoice.FilterObject);
if (invoices != null)
{
MemoryStream invoiceStream = ExcelConverter.InvoiceToExcel(invoices);
var contentType = "application/octet-stream";
var fileName = "Invoice.xlsx";
return File(invoiceStream, contentType, fileName);
}
else
{
ResponseModel.Notification = Utility.CreateNotification("Not Found Anything", Enums.NotificationType.Warning);
return NotFound(ResponseModel);
}
}
catch (Exception ex)
{
NLogger.LogError(ex, "Get Invoice With Excel");
ResponseModel.Notification = Utility.CreateNotification(Helpers.ExceptionMessage(ex), Enums.NotificationType.Error);
return StatusCode(500, ResponseModel);
}
}
Finally the React and axois code as below.
the Service code:
return http.post(
API_BASE_URL + "/Invoice/GetInvoiceWithExcel",
searchInvoice,
{
headers: getHeaders(), // for token and others
responseType: 'blob' // **don't forget to add this**
}
);
};
And the Action method Code is below. Here I use the "file-saver" package to download the file.
import { saveAs } from 'file-saver';
export const getInvoiceWithExcel = invoiceInfo => {
return dispatch => {
dispatch({
type: LOADING_ON
});
InvoiceService.getInvoiceWithExcel(invoiceInfo)
.then(res => {
console.log(res);
let filename = res.headers['content-disposition']
.split(';')
.find((n) => n.includes('filename='))
.replace('filename=', '')
.trim();
let url = window.URL
.createObjectURL(new Blob([res.data]));
saveAs(url, filename);
dispatch({
type: GET_INVOICE_EXCEL_SUCCESS,
payload: ""
});
dispatch({
type: LOADING_OFF
});
dispatch({
type: ON_NOTIFY,
payload: {
...res.data.notification
}
});
})
.catch(err => {
dispatch({
type: GET_INVOICE_EXCEL_FAILED
});
dispatch({
type: LOADING_OFF
});
dispatch({
type: ON_NOTIFY,
payload: {
...Utility.errorResponseProcess(err.response)
}
});
});
};
};

How to download excel/Zip files in Angular 4

I am using angular 4 as frond end and lumen 5.4 as back end.
My requirement is to export some data as excel and zip file.
Using import { saveAs } from 'file-saver/FileSaver'; package for file download.
Angular 4 Code:
downloadExcel() {
const type = 'application/vnd.ms-excel';
const headers = { headers: new Headers({ 'Accept': type }) };
const filename = 'file.xls';
this.http.get('http://10.2.2.109/Download/exportExcel', headers)
.toPromise()
.then(response => this.saveToFileSystem(response, type, filename));
return false;
}
private saveToFileSystem(response, __type, filename) {
const contentDispositionHeader: string = response.headers.get('Content-Disposition');
if (contentDispositionHeader !== null) {
const parts: string[] = contentDispositionHeader.split(';');
//const filename = parts[1].split('=')[1];
const blob = new Blob([response._body], { type: __type });
saveAs(blob, filename);
} else {
alert('Cant download.....');
// handling download condition if content disposition is empty
const blob = new Blob([response._body], { type: __type });
saveAs(blob, filename);
}
}
Lumen Code
public function exportExcel(Request $request) {
$file = storage_path();
$file_name = 'book1.xls';
$headers = [
'Content-type' => 'application/vnd.ms-excel',
'Content-Disposition' => 'attachment;filename="' . $file_name,
'X-Filename' => $file_name,
'Content-Transfer-Encoding' => 'binary',
'Content-Length' => filesize($file . '/' . $file_name),
'Cache-Control' => 'max-age=0',
'Cache-Control' => 'max-age=1',
'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
'Last-Modified' => gmdate('D, d M Y H:i:s') . ' GMT',
'Cache-Control' => 'cache, must-revalidate',
'Pragma' => 'public',
'Set-Cookie' => 'fileDownload=true; path=/',
'Access-Control-Expose-Headers' => 'Content-Length,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma'
];
return response()->download($file . '/' . $file_name, $file_name, $headers);
}
Issues
const contentDispositionHeader: string = response.headers.get('Content-Disposition'); seems always empty.
We cant open downloaded file, shows corrupted message.
It working for text file download
Please help me to resolve this issue. OR specify any other working code//package for angular
Try this:
downloadExcel() {
const type = 'application/vnd.ms-excel';
const filename = 'file.xls';
const options = new RequestOptions({
responseType: ResponseContentType.Blob,
headers: new Headers({ 'Accept': type })
});
this.http.get('http://10.2.2.109/Download/exportExcel', options)
.catch(errorResponse => Observable.throw(errorResponse.json()))
.map((response) => {
if (response instanceof Response) {
return response.blob();
}
return response;
})
.subscribe(data => saveAs(data, filename),
error => console.log(error)); // implement your error handling here
}
The key points are responseType: ResponseContentType.Blob on the RequestOptions and response.blob() when getting back the response.
In general, it's not recommended to access the _body property of the response like this: response._body, but instead you should call the relevant method to get the body content based on its type (like response.blob(), response.json(), etc)
You could use json2csv provided the input data is in JSON format. The output of the function will be CSV which can be opened in Microsoft Excel or Google Sheets.
Install the package:
$ npm install json2csv --save
Add the following to your component:
var json2csv = require('json2csv');
var fields = ['field1', 'field2', 'field3'];
var result = json2csv({ data: myData, fields: fields });

Angular2 + Express.js: Return CSV and offer downloadable file?

I have this code in my ng2 component:
data => {
let blob = new Blob([data], { type: "text/csv" });
FileSaver.saveAs(blob, "data.csv);
}
Then there is a file saved, but the contents of the file read:
Response with status: 200 OK for URL: https://MYURL/URI/data.csv
My backend is node/express, do I have to create a stream for this or does it work with direct file access like in the old days?
This is my backend at the moment, how can I alter it to return a stream?
'use strict';
const Transactions = require('../../models').transaction;
module.exports = function * details(req, res) {
logger.debug(' Get TME details ' + req.currentTME._id);
const transaction = yield Transactions.findOne({
_tme: req.currentTME.id
}).lean();
return res
.json({
status: constants.status.SUCCESS,
timemodel: req.currentTME,
linkedInfo: {
transaction
}
});
};
Full Server Call:
request(url: string, data: any, method: RequestMethod, showErrorDialog = true): Observable<any> {
const requestOptionsArgs: RequestOptionsArgs = {
url: this.baseUrl + url,
method: method,
headers: this.createHeaders(),
body: JSON.stringify(data)
};
this.log.info(this.getCurl(requestOptionsArgs), this);
const requestOptions: RequestOptions = new RequestOptions(requestOptionsArgs);
return this.intercept(
this.http.request(new Request(requestOptions)).timeout(3000),
requestOptionsArgs,
showErrorDialog
).share();
}

How to consume MTOM SOAP web service in node.js?

I need to download or process a file from a soap based web service in node.js.
can someone suggest me on how to handle this in node.js
I tried with 'node-soap' or 'soap' NPM module. it worked for normal soap web service. But, not for binary steam or MTOM based SOAP web service
I want to try to answer this... It's quite interesting that 2 years and 2 months later I can not figure it out how to easily solve the same problem.
I'm trying to get the attachment from a response like:
...
headers: { 'cache-control': 'no-cache="set-cookie"',
'content-type': 'multipart/related;boundary="----=_Part_61_425861994.1525782562904";type="application/xop+xml";start="";start-info="text/xml"',
...
body: '------=_Part_61_425861994.1525782562904\r\nContent-Type:
application/xop+xml; charset=utf-8;
type="text/xml"\r\nContent-Transfer-Encoding: 8bit\r\nContent-ID:
\r\n\r\n....\r\n------=_Part_61_425861994.1525782562904\r\nContent-Type:
application/octet-stream\r\nContent-Transfer-Encoding:
binary\r\nContent-ID:
\r\n\r\n�PNG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0002,\u0000\u0000\u0005�\b\u0006\u0........binary....
I tried ws.js but no solution.
My solution:
var request = require("request");
var bsplit = require('buffer-split')
// it will extract "----=_Part_61_425861994.1525782562904" from the response
function getBoundaryFromResponse(response) {
var contentType = response.headers['content-type']
if (contentType && contentType.indexOf('boundary=') != -1 ) {
return contentType.split(';')[1].replace('boundary=','').slice(1, -1)
}
return null
}
function splitBufferWithPattern(binaryData, boundary) {
var b = new Buffer(binaryData),
delim = new Buffer(boundary),
result = bsplit(b, delim);
return result
}
var options = {
method: 'POST',
url: 'http://bla.blabal.../file',
gzip: true,
headers: {
SOAPAction: 'downloadFile',
'Content-Type': 'text/xml;charset=UTF-8'
},
body: '<soapenv: ... xml request of the file ... elope>'
};
var data = [];
var buffer = null;
var filename = "test.png"
request(options, function (error, response, body) {
if (error) throw new Error(error);
if (filename && buffer) {
console.log("filename: " + filename)
console.log(buffer.toString('base64'))
// after this, we can save the file from base64 ...
}
})
.on('data', function (chunk) {
data.push(chunk)
})
.on('end', function () {
var onlyPayload = splitBufferWithPattern(Buffer.concat(data), '\r\n\r\n') // this will get from PNG
buffer = onlyPayload[2]
buffer = splitBufferWithPattern(buffer, '\r\n-')[0]
console.log('Downloaded.');
})
I am not sure it will works in most of the cases. It looks like unstable code to my eyes and so I'm looking for something better.
Use ws.js
Here is how to fetch the file attachments:
const ws = require('ws.js')
const { Http, Mtom } = ws
var handlers = [ new Mtom(), new Http()];
var request = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' +
'<s:Body>' +
'<EchoFiles xmlns="http://tempuri.org/">' +
'<File1 />' +
'</EchoFiles>' +
'</s:Body>' +
'</s:Envelope>'
var ctx = { request: request
, contentType: "application/soap+xml"
, url: "http://localhost:7171/Service/mtom"
, action: "http://tempuri.org/IService/EchoFiles"
}
ws.send(handlers, ctx, function(ctx) {
//read an attachment from the soap response
var file = ws.getAttachment(ctx, "response", "//*[local-name(.)='File1']")
// work with the file
fs.writeFileSync("result.jpg", file)
})
Two limitations:
No basic auth provided out-of-box, patch required https://github.com/yaronn/ws.js/pull/40
If the file name is an url, you need to apply another patch at mtom.js. Replace:
.
xpath = "//*[#href='cid:" + encodeURIComponent(id) + "']//parent::*"
with:
xpath = "//*[#href='cid:" + id + "']//parent::*"

How to get binary data from ng-file-upload file object?

I'm attempting to use the ng-file-upload directive to provide file upload functionality in my angular app.
I've got it working for the most part - I can select multiple files and loop through to grab the file name and file types. I just can't seem to figure out where the actual binary data of each file is stored in the file object.
I tried using the approach outlined in this post - AngularJS Upload a file and send it to a DB, but that results in a an error that "$q is not defined".
function create_blob(file) {
var deferred = $q.defer();
var reader = new FileReader();
reader.onload = function () {
deferred.resolve(reader.result);
};
reader.readAsDataURL(file);
return deferred.promise;
}
So then I tried the approach outlined in this post - Send an uploaded image to the server and save it in the server, but again I'm running into an error reading "dataURI.split is not a function".
function dataURItoBlob(dataURI) {
var binary = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {
type: mimeString
});
}
The code I'm using is as follows:
function create_blob(file) {
var deferred = $q.defer();
var reader = new FileReader();
reader.onload = function () {
deferred.resolve(reader.result);
};
reader.readAsDataURL(file);
return deferred.promise;
}
function dataURItoBlob(dataURI) {
var binary = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {
type: mimeString
});
}
$scope.uploadFiles = function (files) {
$scope.files = files;
angular.forEach(files, function (file) {
if (file && !file.$error) {
//var reader = new FileReader();
//console.log(reader.readAsDataURL(file));
//var binary = create_blob(file);
var fileBinary = dataURItoBlob(file);
$http({
url: root + '/DesktopModules/ServiceProxy/API/NetSuite/InsertCaseFile',
method: "POST",
//headers: { 'caseId': id, 'fileName': file.name, fileContent: $.base64.encode(file) }
headers: { 'caseId': id, 'fileName': file.name, fileContent: fileBinary }
}).
success(function (data, status, headers, config) {
//if (data == true) {
// getCase();
// $scope.newMessage = "";
// //toaster.pop('success', "", "Message succesfully submitted.",0);
//}
}).
error(function (data, status, headers, config) {
});
file.upload.progress(function (evt) {
file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
});
}
});
}
What am I overlooking?
It depends on what format your DB is accepting for file upload. If it support multipart form data, then you can just use
Upload.upload({file: file, url: my/db/url}).then(...);
if it accepts post requests with file's binary as content of the request (like CouchDB, imgur, ...) then you can do
Upload.http({data: file, url: my/db/url, headers: {'Content-Type': file.type}})...;
if you db just accept json objects and you want to store the file as base64 data url in the database like this question then you can do
Upload.dataUrl(file, true).then(function(dataUrl) {
$http.post(url, {
fileBase64DataUrl: dataUrl,
fileName: file.name,
id: uniqueId
});
})

Resources