Download excel file from page via WebApi call - c#

I'm trying to send a 9MB .xls file as a response from web api controller method. The user will click a button on the page and this will trigger the download via the browser.
Here's what I've got so far but it doesn't work however it doesn't throw any exceptions either.
[AcceptVerbs("GET")]
public HttpResponseMessage ExportXls()
{
try
{
byte[] excelData = m_toolsService.ExportToExcelFile();
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new MemoryStream(excelData);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Data.xls"
};
return result;
}
catch (Exception ex)
{
m_logger.ErrorException("Exception exporting as excel file: ", ex);
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
}
Here is the coffeescript/javascript jquery ajax call from a button click in the interface.
$.ajax(
url: route
dataType: 'json'
type: 'GET'
success: successCallback
error: errorCallback
)
Now that I think about it perhaps the dataType is wrong and shouldn't be json...

Works also as a HTTP GET method, but don't use $ajax, instead use
window.open(url);
C# code:
[HttpGet]
[Route("report/{scheduleId:int}")]
public HttpResponseMessage DownloadReport(int scheduleId)
{
var reportStream = GenerateExcelReport(scheduleId);
var result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StreamContent(reportStream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Schedule Report.xlsx"
};
return result;
}
JS code:
downloadScheduleReport: function (scheduleId) {
var url = baseUrl + 'api/Tracker/report/' + scheduleId;
window.open(url);
}

I had to make a couple of small changes to get this to work
First: Change the method to a post
[AcceptVerbs("POST")]
Second: Change from using the jQuery ajax lib to use a hidden form, here's my service function for doing the hidden form and submitting it.
exportExcel: (successCallback) =>
if $('#hidden-excel-form').length < 1
$('<form>').attr(
method: 'POST',
id: 'hidden-excel-form',
action: 'api/tools/exportXls'
).appendTo('body');
$('#hidden-excel-form').bind("submit", successCallback)
$('#hidden-excel-form').submit()
Hopefully there's a better way to do this but for the time being it's working and downloading the excel file nicely.

I experienced the same problem.
Problem solved with the following:
window.open(url)

It will store the excel file created in a folder in the system and once its sent to Browser, it will be deleted .
//path to store Excel file temporarily
string tempPathExcelFile = AppDomain.CurrentDomain.BaseDirectory + DateTime.Now.Hour + DateTime.Now.Minute +
DateTime.Now.Second + DateTime.Now.Millisecond +
"_temp";
try
{
//Get Excel using Microsoft.Office.Interop.Excel;
Excel.Workbook workbook = ExportDataSetToExcel();
workbook.SaveAs(tempPathExcelFile, workbook.FileFormat);
tempPathExcelFile = workbook.FullName;
workbook.Close();
byte[] fileBook = File.ReadAllBytes(tempPathExcelFile);
MemoryStream stream = new MemoryStream();
string excelBase64String = Convert.ToBase64String(fileBook);
StreamWriter excelWriter = new StreamWriter(stream);
excelWriter.Write(excelBase64String);
excelWriter.Flush();
stream.Position = 0;
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = new StreamContent(stream);
httpResponseMessage.Content.Headers.Add("x-filename", "ExcelReport.xlsx");
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-excel");
httpResponseMessage.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = "ExcelReport.xlsx";
httpResponseMessage.StatusCode = HttpStatusCode.OK;
return httpResponseMessage;
}
catch (Exception ex)
{
_logger.ErrorException(errorMessage, ex);
return ReturnError(ErrorType.Error, errorMessage);
}
finally
{
if (File.Exists(tempPathExcelFile))
{
File.Delete(tempPathExcelFile);
}
}
//Javascript Code
$.ajax({
url: "/api/exportReport",
type: 'GET',
headers: {
Accept: "application/vnd.ms-excel; base64",
},
success: function (data) {
var uri = 'data:application/vnd.ms-excel;base64,' + data;
var link = document.createElement("a");
link.href = uri;
link.style = "visibility:hidden";
link.download = "ExcelReport.xlsx";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
error: function () {
console.log('error Occured while Downloading CSV file.');
},
});
In the end create an empty anchor tag at the end of your html file. <a></a>

For.NetCore you can simply Return type as File.
public IActionResult ExportAsExcel()
{
try
{
using (var ms = new MemoryStream())
{
var data = ExportData(); // Note: This Should be DataTable
data.ConvertAsStream(ms);
ms.Position = 0;
return File(ms.ToArray(), "application/octet-stream", "ExcelReport.xlsx");
}
}
catch (Exception e)
{
_logger.LogError(e.Message, e);
throw;
}
}

This will return a file when you click a particular button
public FileResult ExportXls(){
//the byte stream is the file you want to return
return File(bytes, "application/msexcel")
}

Related

Can't get angular/Core api to download a pdf into a new window

I am trying to open a new tab and display a downloaded pdf from Core 2.2 web API in angular 9
public GeneratePdf(id: string): Observable<Blob> {
return this.http.get( this.urlPdf + '?id=' + id, { responseType: 'blob' });
}
this.dataProvider.GeneratePdf(id).subscribe({
next: (blob) => {
const blobpart = new Blob([blob], { type: 'application/pdf' });
var fileUrl = URL.createObjectURL(blobpart);
let win: any = this.getWindow();
win.open(fileUrl);
},
error: (err) => this.error.handelHttpError(err),
});
API
[HttpGet]
[Route("PDF")]
public async Task<HttpResponseMessage> PDF(Guid Id) {
_logger.LogInformation("Request:" + Request.GetDisplayUrl());
var endpoint = _appSettings.PdfEndpoint;
try {
var param = await _dal.GetPDFParameters(Id, endpoint);
// Get PDF stream
HttpResponseMessage response = await client.GetAsync(param.EndpontUrl);
if(response.IsSuccessStatusCode) {
using(HttpContent content = response.Content) {
var memStream = new MemoryStream();
Stream sourceStream = await content.ReadAsStreamAsync();
sourceStream.CopyTo(memStream);
var result = new HttpResponseMessage(HttpStatusCode.OK) {
Content = new ByteArrayContent(memStream.ToArray())
};
result.Content.Headers.ContentDisposition =
new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") {
FileName = Id.ToString() + ".pdf"
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
} else {
var result = new HttpResponseMessage(HttpStatusCode.BadRequest);
return result;
}
} catch(Exception ex) {
_logger.LogError(ex, "Exception error");
var result = new HttpResponseMessage(HttpStatusCode.InternalServerError);
return result;
}
}
it doesn't throw an error, it just opens a json object in the new tab, and the size of the object looks too small as the pdf content is over 3k bytes
{"version":{"major":1,"minor":1,"build":-1,"revision":-1,"majorRevision":-1,"minorRevision":-1},"content":{"headers":[{"key":"Content-Disposition","value":["attachment; filename=bd94ee98-65be-4c4f-a001-abecaf1a0644.pdf"]},{"key":"Content-Type","value":["application/octet-stream"]}]},"statusCode":200,"reasonPhrase":"OK","headers":[],"requestMessage":null,"isSuccessStatusCode":true}
update, there was a small error on the Blob, I was not passing in the blobpart to the url.CreateObjecturl Now the app loads the new tab, but states an invalid pdf as there is no content. I know the pdf bytes are going into the content of the api result as i have checked it. and converted it to a string to ensure it's a pdf, which it is.
Thanks for taking the time too look.

Read blob data that comes from asp.net core to Angular 7

Good day. Please help me with my problem, I was not able to read properly the data that comes from backend to my angular. What im doing here is downloading a file that comes from the backend(asp.net core)
This is the code for my controller
[HttpPost("Document")]
public HttpResponseMessage Document()
{
try
{
var file = Request.Form.Files;
var fileData = _documentToPdf.DocumentToPdf(file[0].OpenReadStream());
HttpResponseMessage response = null;
response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new ByteArrayContent(fileData.ToArray())
};
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "pdf.pdf";
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentLength = fileData.Length;
Console.WriteLine("file data size: " + fileData.Length);
return response;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
throw;
}
}
And this is my angular code
uploadAndProgress(files: File[]){
console.log(files)
var formData = new FormData();
Array.from(files).forEach(f => formData.append('file',f))
this.http.post('http://localhost:5000/api/DocumentToPdf/Document', formData, {reportProgress: true, responseType: 'blob', observe: 'events'})
.subscribe(event => {
if (event.type === HttpEventType.UploadProgress) {
this.percentDone = Math.round(100 * event.loaded / event.total);
} else if (event instanceof HttpResponse) {
this.uploadSuccess = true;
console.log('Success');
console.log('event: ' + event);
this.downloadFile(event, 'download.pdf');
}
});
}
downloadFile(data: any, filename: string) {
const blob = new Blob([data], { type: 'application/octet-stream' });
console.log(blob);
saveAs(blob, filename);
}
The code Console.WriteLine("file data size: " + fileData.Length); returns a 65651 but the code in angular which is console.log(blob); it returns
size: 15
type: "application/octet-stream"
So why is it that they are not the same? So how to i properly read the blob that comes from asp.net to my angular so i can download it. Thank you so much
You can do like this
In your service
url: string = "some url";
getBlob(): Observable<Blob> {
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
});
return this.httpClient.get<Blob>(this.url, {headers: headers, responseType: 'blob' as 'json' });
}
Then in your component
imageBlobUrl: string = null;
getImage() : void {
this.blobService.getBlob()
.subscribe((val) => {
this.createImageFromBlob(val);
});
}
createImageFromBlob(image: Blob) {
let reader = new FileReader();
reader.addEventListener("load", () => {
this.imageBlobUrl = reader.result;
}, false);
if (image) {
reader.readAsDataURL(image);
}
}
Then in your view
<img [src]="imageBlobUrl">

Web API upload file then process and download file in one web API request

I have looked through the posts on here and throughout Google and can't seem to find a solution to this.
I want to do this without using Node.js if I can. If I cannot then I will do it because it is required.
I know how to upload a file such as:
[HttpPost]
public HttpResponseMessage PostEncryption()
{
var httpPostedFile = HttpContext.Current.Request.Files["file"];
//do some editing on the file
}
And I know how to download a file via get
private HttpResponseMessage Get(HttpResponseMessage response)
{
var txtString= "a bunch of random stuff";
var result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StringContent(txtString, Encoding.UTF8, "application/txt");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "test.txt"
};
return result;
}
What I am trying to do is pull a file in then do some processing and force the download of the file in the browser. Is this possible through the web API and if so, how would I accomplish it?
Here is my code currently as asked for by slaks, at this point i am just trying to get a post method to download a file...
[HttpPost]
public HttpResponseMessage Post()
{
var txtString= "a bunch of random stuff";
var result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StringContent(txtString, Encoding.UTF8, "application/txt");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "test.txt"
};
return result;
}
$('#uploadFile').on('click', function () {
var data = new FormData();
var files = $("#file").get(0).files;
// Add the uploaded content to the form data collection
if (files.length > 0) {
data.append("file", files[0]);
}
$.ajax({
type: "POST",
url: "/apifile/post",
contentType: false,
processData: false,
data: data
});
});

download file from asp.net web api

i am trying to download a file (.docx) from asp.net web api.
Since i already have a document in the server i set the path to existing one and then i follow something sugested on stackoverflow and do this:
docDestination is my path.
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(docDestination, FileMode.Open, FileAccess.Read);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
return result;
after that on my client side i try to do this:
.then(response => {
console.log("here lives the response:", response);
var headers = response.headers;
var blob = new Blob([response.body], { type: headers['application/vnd.openxmlformats-officedocument.wordprocessingml.document'] });
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "Filename";
link.click();
}
this is what i get on my response
what i get:
any help?
Just add ContentDisposition to your response header with value of attachment and the browser will interpret it as a file that needs to be download
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(docDestination, FileMode.Open,FileAccess.Read);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "document.docx"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
return result;
Take a look in this link for more information in ContentDisposition header
Change return type of your method. You can write method something like this.
public FileResult TestDownload()
{
FileContentResult result = new FileContentResult(System.IO.File.ReadAllBytes("YOUR PATH TO DOC"), "application/msword")
{
FileDownloadName = "myFile.docx"
};
return result;
}
In client side, you just need to have a link button. Once you click on the button, file will be downloaded. Just write this line in cshtml file. replace controller name with your controller name.
#Html.ActionLink("Button 1", "TestDownload", "YourCOntroller")
When you have a stream open, you want to return it's content as a file
[HttpGet]
public async Task<FileStreamResult> Stream()
{
var stream = new MemoryStream(System.IO.File.ReadAllBytes("physical path of file"));
var response = File(stream, "Mime Type of file");
return response;
}
You use it when you have a byte array you would like to return as a file
[HttpGet]
public async Task<FileContentResult> Content()
{
var result = new FileContentResult(System.IO.File.ReadAllBytes("physical path of file"), "Mime Type of file")
{
FileDownloadName = "Your FileName"
};
return result;
}
when you have a file on disk and would like to return it's content (you give a path)-------------only in asp.net core
[HttpGet]
public async Task<IActionResult> PhysicalPath()
{
var result = new PhysicalFileResult("physical path of file", "Mime Type of file")
{
FileDownloadName = "Your FileName",
FileName = "physical path of file"
};
return result;
}

Download excel file using webapi and angularjs

I'm trying to download a closedXml excel file in a webapi/angularjs application.
I'm returning the data from the webapi controller on the server using:
HttpResponseMessage result = new HttpResponseMessage();
result = Request.CreateResponse(HttpStatusCode.OK);
MemoryStream stream = GetStream(workbook);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-excel");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Download.xlsx"
};
return result;
and then saving it on the client using:
$scope.openExcel = function (data, status, headers, deferred) {
var type = headers('Content-Type');
var disposition = headers('Content-Disposition');
if (disposition) {
var match = disposition.match(/.*filename=\"?([^;\"]+)\"?.*/);
if (match[1])
defaultFileName = match[1];
}
defaultFileName = defaultFileName.replace(/[<>:"\/\\|?*]+/g, '_');
var blob = new Blob([data], { type: type });
saveAs(blob, defaultFileName);
Excel says the file is in a different format than specified by the extension and then doesn't open properly.
On projects I work on, I make a Controller for files(not an ApiController)
public class FilesController : Controller
{
public FileResult GetFile(/*params*/)
{
// get fileBytes
var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
return base.File(fileBytes, contentType, "Download.xlsx");
}
}
and then from angular, I open the file like this
$window.open("/Files/GetFile/" /*+ params*/);

Categories