1

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.

api call screenshot - Chrome Dev tools

Max
  • 21
  • 1
  • 5
  • Save the generated PDF file to disk and send the URL of the PDF file to the UI to download – ihimv Oct 26 '20 at 09:53
  • I am using a serverless approach, the web api is running in a Lambda function. So this is not my preferred way. – Max Oct 26 '20 at 10:09
  • I too have faced a similar issue, but with `xlsx` file. I uploaded the generated file to Azure Storage and returned the URL to the storage file. – ihimv Oct 26 '20 at 10:11
  • You can try to use 'saveAs' function provided by 'file-saver' package (npm). After getting results from getOrderPDF() call, you can call 'saveAs(new Blob([x], { type: "application/pdf" }), 'Test.pdf)' to save the file locally. – Joseph Wu Oct 26 '20 at 10:54
  • Thanks for the approach. I've just tested it but it's still not working. Same issue as before. – Max Oct 26 '20 at 13:20

2 Answers2

1

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

harpal
  • 426
  • 4
  • 12
0

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.

api-call

Max
  • 21
  • 1
  • 5