Requesting XLSX file through js to authorized asp.net web api - c#

I have ajax calling my web api to get a xlsx file that is generated when it is called. The xlsx file is in a memory stream that I know is correct because I downloaded it straight from the server and had no issue. However when I try to download the file through the browser it says that the file is invalid. I'm using downloadjs to download the data.
How I return the Stream
public HttpResponseMessage Function_Name() {
var stream = getStream();
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
How I get the Stream
$.ajax({ url: "urlToData", headers: {
'Authorization': 'Authorization'
},
success: function (data) { download(data, "test.xlsx"); }
});
When I run this code I get a file however that file does not read in excel. The file is also 15.3 KB where the functioning file downloaded straight from the server is actually 10.0kb

I was feeling desperate and tried this out: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
When I used XMLHttpRequest() it worked correctly.
var xhr = new XMLHttpRequest()
xhr.open('GET', 'url', true);
xhr.setRequestHeader('Authorization', 'Authorization');
xhr.responseType = 'blob';
xhr.onload = function(e) {
if(this.status == 200) {
var blob = this.response;
download(this.response, "test.xlsx")
}
}
xhr.send();

Related

How to convert asp web api byte[] to pdf file in Angular 8?

I've been having some problems when I try to convert a byte[] coming from C# into a pdf file using Angular.
When I perform the conversion the file is created but is corrupted and it can't be opened "We can't open this file", the file content is just basic text "Hello world".
Please let me know if you have any answer to this problem, the code I am using is below and I also try using "import { saveAs } from 'file-saver';" but it didn't work.
// THE WEB API CONTROLLER THAT GETS THE PDF BYTE ARRAY
[HttpPost("CreatePDF")]
public async Task<HttpResponseMessage> CreatePDF([FromBody] PDFContent pdfContent)
{
byte[] pdf = await _pdfGenerator.GeneratePDF(pdfContent.HtmlContent);
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent(pdf);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
// UI SERVICE LAYER THAT CALLS THE SERVER SIDE
createPdfReport(pdfContent: PDFContent): Observable<any> {
return this.restApi.create("CreatePDF",pdfContent).pipe(
tap(pdf =>
console.log('pdf = ' + pdf)
));
}
// THE COMPONENT THAT CALLS THE SERVICE LAYER AND WAITS FOR THE RESPONSE
this.historyService.createPdfReport(pdfContent).subscribe(data => {
var file = new Blob(data, { type: 'application/octet-stream' })
var fileURL = URL.createObjectURL(data);
const link = document.createElement('a');
link.setAttribute('target', '_blank');
link.setAttribute('href', fileURL);
link.setAttribute('download', `file.pdf`);
document.body.appendChild(link);
link.click();
link.remove();
});
}
This is the response I get in Angular after the controller returns the byte[]:

How to Export an Excel File using C#

Helo, i already search on Google and here (StackOverflow) but no any solution i complete.
I Have a Excel file in my folder and i need to create a method on my controller to download this file.
And in my React Web Site i need to get this file to user computer.
I try to use ActionResult, FileStreamResult, HttpResponseMessage and other, read file from folder with File.ReadAllbytes, put the Header on response.
On the final i get this.
{ FileContents: "allcontentoffilehere....", Contenttype: "application/octet-stream", FileDownloadName: "filename.xls"}
And using this JavaScript do download:
var bytes = new Uint8Array(responseDownloadFile.data.FileContents);
var blob = new Blob([bytes], {
type: responseDownloadFile.data.ContentType
});
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = responseDownloadFile.data.FileDownloadName;
document.body.appendChild(link);
link.click();
But the file when download is corrupted.
Any on can help me?
Try to return HttpResponseMessage type on your API
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent([file bytes]);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/file type");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "filename.xlsx"
};
return result;
And on your Front end execute code:
window.location.href = "link to your api endpoint to download excel";

Angular - How to download file from Web API on subscribe()

In Angular I'm trying to download an excel file from my Web API Server
If I call my API from an <a> component just like this:
Download
The file downloads fine and without handling the response I get the desired behaviour:
How can I get the same result making the request from my Angular DownloadService and handling the result on .subscribe()?
this.downloadService.download()
.subscribe(file => {/*What to put here for file downloading as above*/});
Note that the server creates the response like this:
byte[] b = File.ReadAllBytes(HttpRuntime.AppDomainAppPath + "/files/" + "file.xlsx");
var dataStream = new MemoryStream(b);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(dataStream);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "File";
response.Content.Headers.ContentType = new MediaTypeHeaderValue(MimeMapping.GetMimeMapping("file.xlsx"));
return response;
Thanks in advance! :)
try this work around:
.subscribe(file =>
{
const a = document.createElement('a');
a.setAttribute('type', 'hidden');
a.href = URL.createObjectURL(file.data);
a.download = fileName + '.xlsx';
a.click();
a.remove();
});
The comment above should work, but here is another way
.subscribe(file) {
const blob = new Blob([file], { type: 'text/csv' }); // you can change the type
const url= window.URL.createObjectURL(blob);
window.open(url);
}
You can add js-file-download library which supports all browsers
npm install js-file-download --save
you can use window.URL.createObjectURL(blob); but IE does not support it...
here is my code for downloading Excel files from the Backend
import axios from 'axios';
import * as FileSaver from 'file-saver';
const result: any = await axios.create().post("http://localhost:8080/file",
{
responseType: "arraybuffer",
});
let blob = new Blob([result.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
FileSaver.saveAs(blob, "export.xlsx");

Browser doesn't show progress bar when downloading a file

I have an Angular application with an ASP.NET Web API.
I want to download a file stored on my server. Currently, this is the code I have:
[HttpGet]
[Route("downloadFile")]
[JwtAuthentication] //Only a connected user can download the file
public async Task<HttpResponseMessage> DownloadFile(string path)
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var fileStream = File.OpenRead(path);
result.Content = new StreamContent(fileStream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentLength = fileStream.Length;
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileStream.Name,
Size = fileStream.Length
};
return result;
}
And in my Angular code:
// file-navigation.service.ts
downloadFile(file: FileElement) {
const data = { path: this.formatPath(true) + file.name };
return this.http.get(this.apiUrl + '/downloadFile', { params: data, responseType: 'blob' });
}
// file-navigation.component.ts
this.fileNavigationService.downloadFile(element).subscribe(result => {
this.generateDownload(element, result, false);
});
generateDownload(element: FileElement, blob: Blob, isArchive: boolean) {
const fileName = element != null ? element.name : 'Archive';
if (navigator.appVersion.toString().indexOf('.NET') > 0) {
window.navigator.msSaveBlob(blob, fileName + (isArchive ? '.zip' : ''));
} else {
const link = document.createElementNS(
'http://www.w3.org/1999/xhtml',
'a'
);
(link as any).href = URL.createObjectURL(blob);
(link as any).download = fileName + (isArchive ? '.zip' : '');
document.body.appendChild(link);
link.click();
setTimeout(function () {
document.body.removeChild(link);
link.remove();
}, 100);
}
}
With this, I successfully download a file from the server.
However, the download bar in Chrome only appears when the download is done. So if the file is too big, the user won't get any indicator that his file is currently being downloaded.
Below is a screenshot of a 16Mb file being downloaded. The server is currently sending data but the download bar doesn't appear.
Then, once the download has completed, the file appears in the download bar at the bottom of the screen.
How can I send the file to the browser so that it shows this indicator to the user?
Thank you very much.
EDIT:
As #CodeCaster pointed out, redirecting to the URL could work, however, my URL is protected so that only connected users can download the file.
On Angular side, just use anchor tag and pass the API URL in href attribute.
<a href = {this.apiUrl + '/downloadFile' + '?' + 'your params'}>Download</a>
and also on server side before streaming data, make sure you have set the following response header.
res.setHeader('content-length',data.ContentLength) (optional)
res.setHeader('content-type', mimetype);
res.setHeader('content-disposition', 'attachment; filename');

Download Zip Archive from WebAPI in Response to POST Request

I'm trying to deliver a zip archive in response to an AJAX POST request made from Axios to WebAPI.
On the client side I have
import AjaxDownload from "../../data/AjaxDownload";
AjaxDownload.post(id, pageRecords, {
responseType: "blob"
}).then((response) => {
let blob = new Blob([response.data], { type: extractContentType(response) }),
url = window.URL.createObjectURL(blob);
window.open(url, "_self");
}).catch((error) => {
// ...
}).then(() => {
// ...
});
function extractContentType(response: AxiosResponse): string {
return response.headers["content-type"] || "";
}
// AjaxDownload:
import * as axios from "axios";
import { apiUrl } from "./Ajax";
const ajax = axios.default.create({
baseURL: new URL("download", apiUrl).toString(),
timeout: 3600000 // 1 hour
});
export default ajax;
That posts to the following WebAPI method - and the POST part of that client-side logic works exactly as expected.
[HttpPost]
[Route("download/{id:guid}")]
public async Task<HttpResponseMessage> Download(Guid id, [FromBody] IEnumerable<PageRecord> pageRecords)
{
var stream = await _repo.GetAsArchiveStream(id,
pageRecords,
true).ConfigureAwait(false);
stream.Position = 0;
var result = new HttpResponseMessage(HttpStatusCode.OK) {Content = new StreamContent(stream)};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {FileName = $"{...}.zip"};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); // "application/zip" has same result
result.Content.Headers.ContentLength = stream.Length;
return result;
}
However, the browser displays the result.Content as a JSON object, without the zip archive. I assume that the it's displaying as JSON because the request mentions JSON, but why does it appear to ignore the binary content - particularly as the Content-Type header details the type of content?
And as you can see, the JavaScript is also expecting to read in the content as a blob.
I don't see how my code differs meaningfully from this answer - please explain if there is a crucial difference.
On the server-side, I've also tried returning...
return new FileStreamResult(stream, "application/zip");
The problem with this approach is that there's no way to set a filename. Firefox does download the zip albeit with a random name while Chrome doesn't appear to download anything at all.
There must be a way to do this, right? To POST a request to a WebAPI method which returns a zip archive, and the client-side then presents a Save dialog? What am I missing?
I managed to solve this simply by returning the zip from the controller action using...
return File(stream,
"application/zip",
"FILENAME.zip");
And in the client-side code I can fetch the filename from the headers using some JavaScript found in this SO answer.
let blob = new Blob([response.data], { type: extractContentType(response) }),
downloadUrl = window.URL.createObjectURL(blob),
filename = "",
disposition = response.headers["content-disposition"];
if (disposition && disposition.indexOf("attachment") !== -1) {
let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/,
matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, '');
}
}
var a = document.createElement("a");
// safari doesn't support this yet
if (typeof a.download === 'undefined') {
window.location.href = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}

Categories

Resources