Hello I am trying to download a pdf from a report that i create on the server. When I create it I save it on the server for a moment then I make a second call and download it.
The following is the code that is on the web API:
var rootDirectory = HostingEnvironment.MapPath("~");
var filePath = $#"{rootDirectory}\Temp\{file}.pdf";
var bytes = File.ReadAllBytes(filePath);
File.Delete(filePath);
var memoryStream = new MemoryStream(bytes);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(memoryStream.ToArray())
};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "report.pdf"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return result;
then I get back to the typescript and here is the code that I use do download it:
this.reportsService
.download(id)
.subscribe(r => {
let pdf =r._body;
var pdfName = `${this.selectedReport.name}.pdf`;
let blob = new Blob([pdf], { type: 'application/pdf' });
window.open(URL.createObjectURL(blob));
});
});
I should mention that when I open the pdf from where it is saved on the server it is correct.
Can someone please tell me what I'm missing?
Related
I am struggeling with this already a few days. I found many threads on Stackoverflow but nothing could solve my issue.
I have the following Web API which should return a simple pdf file:
[HttpGet("pdf/{id}")]
public async Task<IActionResult> GetOrderAsPDF([FromRoute] int id)
{
MemoryStream ms = new MemoryStream();
PdfWriter writer = new PdfWriter(ms);
PdfDocument pdf = new PdfDocument(writer);
writer.SetCloseStream(false);
Document document = new Document(pdf);
Paragraph header = new Paragraph("HEADER")
.SetTextAlignment(TextAlignment.CENTER)
.SetFontSize(20);
document.Add(header);
document.Close();
ms.Position = 0;
return File(ms, "application/pdf", "test.pdf");
}
service.ts
async getOrderPDF(id):Promise<Blob>{
const url = `${this.baseUrl}/orders/pdf/${id}`;
const key: string = await this.getKey();
console.log('key:', key);
let headers = new HttpHeaders();
headers = headers.append('Authorization', [key]);
return this.http.get(url, {responseType: 'blob', headers: headers}).toPromise();
}
component.ts
exportPDF(div_id: string){
this.backhausService.getOrderPDF(2).then(x => {
var newBlob = new Blob([x], { type: "application/pdf" });
console.log(newBlob);
const data = window.URL.createObjectURL(newBlob);
var link = document.createElement('a');
link.href = data;
link.download = "Test.pdf";
link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
});
}
This code saves the file "Test.pdf" but I am not able to open it. It says "Failed to Load PDF Document".
When I open the file with Notepad++ it shows me the PDF file as Base64 encoded string.
Content of Test.pdf
JVBERi0xLjcKJeLjz9MKNSAwIG9iago8PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDU2Pj5zdHJlYW0KeJwr5HIK4dJ3M1QwMlAISeMyMjXVszBWMLcw0LOwUAhJ4dLwcHV0cQ3SDMnicg3hCuQCAPm7ClwKZW5kc3RyZWFtCmVuZG9iago0IDAgb2JqCjw8L0NvbnRlbnRzIDUgMCBSL01lZGlhQm94WzAgMCA1OTUgODQyXS9QYXJlbnQgMiAwIFIvUmVzb3VyY2VzPDwvRm9udDw8L0YxIDYgMCBSPj4+Pi9UcmltQm94WzAgMCA1OTUgODQyXS9UeXBlL1BhZ2U+PgplbmRvYmoKMSAwIG9iago8PC9QYWdlcyAyIDAgUi9UeXBlL0NhdGFsb2c+PgplbmRvYmoKMyAwIG9iago8PC9DcmVhdGlvbkRhdGUoRDoyMDIwMTAyNjA5Mzg1NiswMCcwMCcpL01vZERhdGUoRDoyMDIwMTAyNjA5Mzg1NiswMCcwMCcpL1Byb2R1Y2VyKGlUZXh0riA3LjEuMTMgqTIwMDAtMjAyMCBpVGV4dCBHcm91cCBOViBcKEFHUEwtdmVyc2lvblwpKT4+CmVuZG9iago2IDAgb2JqCjw8L0Jhc2VGb250L0hlbHZldGljYS9FbmNvZGluZy9XaW5BbnNpRW5jb2RpbmcvU3VidHlwZS9UeXBlMS9UeXBlL0ZvbnQ+PgplbmRvYmoKMiAwIG9iago8PC9Db3VudCAxL0tpZHNbNCAwIFJdL1R5cGUvUGFnZXM+PgplbmRvYmoKeHJlZgowIDcKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMjcwIDAwMDAwIG4gCjAwMDAwMDA1NjEgMDAwMDAgbiAKMDAwMDAwMDMxNSAwMDAwMCBuIAowMDAwMDAwMTM3IDAwMDAwIG4gCjAwMDAwMDAwMTUgMDAwMDAgbiAKMDAwMDAwMDQ3MyAwMDAwMCBuIAp0cmFpbGVyCjw8L0lEIFs8MDY0YjIxMjQwNmY5Y2FlMmNiMWVjNzgyM2ZmNzkwNjQ+PDA2NGIyMTI0MDZmOWNhZTJjYjFlYzc4MjNmZjc5MDY0Pl0vSW5mbyAzIDAgUi9Sb290IDEgMCBSL1NpemUgNz4+CiVpVGV4dC03LjEuMTMgZm9yIC5ORVQKc3RhcnR4cmVmCjYxMgolJUVPRgo=
So what am I doing wrong?
Thanks for your contribution in advance.
UPDATE
Here a screenshot of my api call response from the chrome dev tools.
Can you try the #joseph answer with little bit of change.
I have verified it with your data. and it is working fine.
import FileSaver
import * as FileSaver from 'file-saver';
and then
exportPDF(div_id: string){
this.backhausService.getOrderPDF(2).then(data => {
const byteCharacters = atob(data);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], {type: "application/pdf"});
FileSaver.saveAs(blob, 'test.pdf');
});
}
Hopefully it will work for you also.
Here is working example with your data. https://stackblitz.com/edit/angular-7-master-v7ehpv?file=src/app/app.component.html
For more on Base64 to blob https://stackoverflow.com/a/16245768/6310485
Thanks for all your help. I finally got it solved.
What I didn't write into my question as I didn't know it was relevant, is that I am using an AWS API Gateway. That's where the problem was because API Gateway needs to be configured to support binay. See the aws docs.
As I wanted to have the api as simple as possible I've changed the code as follows to return the base64 encoded file in quotes.
API endpoint
[HttpGet("pdf/{id}")]
public async Task<IActionResult> GetOrderAsPDF([FromRoute] int id)
{
MemoryStream ms = new MemoryStream();
PdfWriter writer = new PdfWriter(ms);
PdfDocument pdf = new PdfDocument(writer);
writer.SetCloseStream(false);
Document document = new Document(pdf);
Paragraph header = new Paragraph("HEADER")
.SetTextAlignment(TextAlignment.CENTER)
.SetFontSize(20);
document.Add(header);
document.Close();
ms.Position = 0;
return Ok(ms.GetBuffer());
}
Service.ts
async getOrderPDF(id):Promise<string>{
const url = `${this.baseUrl}/orders/pdf/${id}`;
const key: string = await this.getKey();
let headers = new HttpHeaders();
headers = headers.append('Authorization', [key]);
return this.http.get<string>(url, {headers}).pipe(catchError(this.handleError<string>('getOrdersCustomer'))).toPromise();
}
Component.ts
exportPDF(div_id: string){
this.backhausService.getOrderPDF(2).then(x => {
console.log(x);
const data = `data:application/pdf;base64,${x}`;
var link = document.createElement('a');
link.href = data;
link.download = "test.pdf";
link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
});
}
Through the change to the backend code the api call response looks as follows.
I am using entity framework(WebAPI/C#/VS2019) and using below code to save file and display file.
Save Code
Document document = new Document();
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(attachment_data); //attachment_data is string
document.document_content=plainTextBytes;//document_content is byte[]
db.Documents.Add(document);
db.SaveChanges();
Below code is used to show saved file
[Route("api/documents/download")]
[HttpGet]
public HttpResponseMessage Download(int documentid)
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var document = db.Documents.Where(e => e.id.Equals(documentid)).FirstOrDefault();
if (document != null)
{
var stream = new System.IO.MemoryStream(document.document_content);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = document.name
};
}
return result;
}
Both Save file and downloading file is working fine.
The issue is how to encrypt the file content before Saving to Database?I saw few SO links but most of the methods are taking string as input and string as output.Not sure which one will fit in this scenario.
I'm trying to return a file in a ASP.NET Web API Controller. This file is a dynamically-generated PDF saved in a MemoryStream.
The client (browser) receives the file successfully, but when I open the file, I see that all the pages are totally blank.
The thing is that if I take the same MemoryStream and write it to a file, this disk file is displayed correctly, so I assume that the problem is related to the file transfer via Web.
My controller looks like this:
[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
MemoryStream memStream = new MemoryStream();
PdfExporter.Instance.Generate(memStream);
memStream.Position = 0;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent(memStream.ToArray()); //OR: new StreamContent(memStream);
return result;
}
Just to try, if I write the stream to disk, it's displayed correctly:
[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
MemoryStream memStream = new MemoryStream();
PdfExporter.Instance.Generate(memStream);
memStream.Position = 0;
using (var fs = new FileStream("C:\\Temp\\test.pdf", FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
memStream.CopyTo(fs);
}
return null;
}
The differences are:
PDF saved on disk: 34KB
PDF transferred via web: 60KB (!)
If I compare both files contents, the main differences are:
File Differences
On the left is the PDF transferred via web; on the right, the PDF saved to disk.
Is there something wrong with my code?
Maybe something related to encodings?
Thanks!
Well, it turned out to be a client (browser) problem, not a server problem. I'm using AngularJS in the frontend, so when the respose was received, Angular automatically converted it to a Javascript string. In that conversion, the binary contents of the file were somehow altered...
Basically it was solved by telling Angular not to convert the response to a string:
$http.get(url, { responseType: 'arraybuffer' })
.then(function(response) {
var dataBlob = new Blob([response.data], { type: 'application/pdf'});
FileSaver.saveAs(dataBlob, 'myFile.pdf');
});
And then saving the response as a file, helped by the Angular File Saver service.
I guess you should set ContentDisposition and ContentType like this:
[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
MemoryStream memStream = new MemoryStream();
PdfExporter.Instance.Generate(memStream);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(memStream.ToArray())
};
//this line
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = "YourName.pdf"
};
//and this line
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}
Try this
[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
MemoryStream memStream = new MemoryStream();
PdfExporter.Instance.Generate(memStream);
//get buffer
var buffer = memStream.GetBuffer();
//content length for header
var contentLength = buffer.Length;
var statuscode = HttpStatusCode.OK;
var response = Request.CreateResponse(statuscode);
response.Content = new StreamContent(new MemoryStream(buffer));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
response.Content.Headers.ContentLength = contentLength;
ContentDispositionHeaderValue contentDisposition = null;
if (ContentDispositionHeaderValue.TryParse("inline; filename=my_filename.pdf", out contentDisposition)) {
response.Content.Headers.ContentDisposition = contentDisposition;
}
return response;
}
I'm using WebApiContrib.Formatting.xlsx to create and download a Microsoft Excel file. My problem is that when called from the client the file can't be opened and several method were tried. When dealing with PDF the problem was resolved converting the output to Byte64 like this:
var response = new HttpResponseMessage
{
Content = new StringContent(Convert.ToBase64String(output.ToArray()))
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Report.pdf"
};
return response;
In the case of WebApiContrib.Formatting.xlsx I have an IEnumerable. How can convert it the same way as the PDF.
var subscriptionExcelModels = subscriptionPlansConverted as SubscriptionExcelModel[] ?? subscriptionPlansConverted.ToArray();
This is my snippet of the last part of the code for the excel file:
var ret = Request.CreateResponse(HttpStatusCode.OK,subscriptionExcelModels);
ret.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
ret.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Report.xlsx"
};
return ret;
So I'm using the fancy EPPlus library to write an Excel file and output it to the user to download. For the following method I'm just using some test data to minimize on the code, then I'll add the code I'm using to connect to database later. Now I can download a file all fine, but when I go to open the file, Excel complains that it's not a valid file and might be corrupted. When I go to look at the file, it says it's 0KB big. So my question is, where am I going wrong? I'm assuming it's with the MemoryStream. Haven't done much work with streams before so I'm not exactly sure what to use here. Any help would be appreciated!
[Authorize]
public ActionResult Download_PERS936AB()
{
ExcelPackage pck = new ExcelPackage();
var ws = pck.Workbook.Worksheets.Add("Sample1");
ws.Cells["A1"].Value = "Sample 1";
ws.Cells["A1"].Style.Font.Bold = true;
var shape = ws.Drawings.AddShape("Shape1", eShapeStyle.Rect);
shape.SetPosition(50, 200);
shape.SetSize(200, 100);
shape.Text = "Sample 1 text text text";
var memorystream = new MemoryStream();
pck.SaveAs(memorystream);
return new FileStreamResult(memorystream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { FileDownloadName = "PERS936AB.xlsx" };
}
Here's what I'm using - I've been using this for several months now and haven't had an issue:
public ActionResult ChargeSummaryData(ChargeSummaryRptParams rptParams)
{
var fileDownloadName = "sample.xlsx";
var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var package = CreatePivotTable(rptParams);
var fileStream = new MemoryStream();
package.SaveAs(fileStream);
fileStream.Position = 0;
var fsr = new FileStreamResult(fileStream, contentType);
fsr.FileDownloadName = fileDownloadName;
return fsr;
}
One thing I noticed right off the bat is that you don't reset your file stream position back to 0.