Retrieve PDF files from .Net Core Web API using Angular

I've been struggling with this issue for several days now. Despite searching through many threads on Stackoverflow, I couldn't find a solution that worked for me.

Below is the Web API code meant to 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 }));
  });
}

Although the code successfully saves the file as "Test.pdf", I'm unable to open it and receive an error message saying "Failed to Load PDF Document". When inspecting the file with Notepad++, it appears to be a Base64 encoded string.

JVBERi0xLjcKJeLjz9MKNSAwIG9iago8PC9GaWx0ZXI.... (Base64 encoded content)

Any ideas on what might be causing this issue?

UPDATE

Here's a screenshot of the response from the API call in Chrome Dev Tools:

https://i.sstatic.net/TsYpL.png

Answer №1

Please attempt the solution provided by @joseph with a few modifications.

I have tested it using your data and confirmed that it is functioning correctly. 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 this solution will also work for you. Here is a functional example with your data. https://stackblitz.com/edit/angular-7-master-v7ehpv?file=src/app/app.component.html

For more information on converting Base64 to blob, visit

Answer №2

Thank you so much for your assistance. I have successfully resolved the issue.

Something I overlooked mentioning in my initial question, which turned out to be crucial, is that I am utilizing an AWS API Gateway. The problem stemmed from the fact that the API Gateway needed to be configured to support binary data. For more information, refer to the AWS documentation.

To streamline the API, I made adjustments to the code to return the base64 encoded file enclosed 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 }));
  });
}

By updating the backend code, the response from the API call now appears as follows.

https://i.sstatic.net/zygTp.png

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using the --prod flag in Ionic 3 app on Android 7 results in the Keyboard not being displayed

After running the command ionic cordova run android --device, everything functions properly. However, when attempting the same command with the --prod flag, the input click fails to display the keyboard despite implementing the (onFocus) attribute in the & ...

Correctly inputting the 'in' statement - Avoid using a primitive value on the right side of an 'in' statement

I am currently facing an issue with my React code where I am getting the error message: The right-hand side of an 'in' expression must not be a primitive.. I am unsure about how to resolve this problem effectively: // The goal is to allow nu ...

In Angular 9, when using ngOnChange, the attribute SimpleChanges.firstChange will always be false even if the value has been

There is a specific task I am trying to accomplish with my component. I need to execute a certain action only the first time the @Input property changes. However, I have encountered an issue where the value of changes.firstChange always returns as false, r ...

Capture a complete screen shot using the WebBrower component

My goal is to take a full-page screenshot of any website that a user is currently viewing using the WebBrowser component. Currently, I am only able to capture what the user sees within the WebBrowser, resulting in a screenshot image the size of the webpag ...

Having Trouble Locating Elements with Selenium in C# and it's Causing Delay

Currently utilizing Selenium in C# for testing a webpage. Wondering if there's a method to swiftly trigger a fail if the element isn't located on the page? Encountering lengthy delays in Selenium tests when an HTML element is missing, which eve ...

Unknown type encountered when using v-for with Vue3 item

I'm currently going through a Laravel bootcamp and following along with the tutorial. However, I encountered an error when trying to display the model with VueJS using v-for loop. Here is my code: type ChirpModel = { id: number, message: string, ...

There is no value inputted into the file

I'm facing a small issue while trying to retrieve the value from my input of type="file". Here is the code snippet: <tr ng-repeat="imagenDatos in tableImagenesPunto | filter: busquedaDatosPunto " > <td>PNG</td> <td>{{imag ...

Putting Tailwind pruning to the test in a React application using Jest

Is there a way to test Tailwind's pruning using Jest without the need for custom postcss configuration? Can it be done by solely implementing the default webpack config created by CRA 5 (as explained here)? It appears that Tailwind is not applying st ...

Exploring the process of linking MatPaginator to a server-sourced datasource within an Angular Material table

In my Angular 5 project, I am utilizing Angular Material table to create a grid. The data is fetched from an HTTP request and stored in a variable named dataSourceNew within the view.ts file. Since dataSourceNew has dynamic content and structure, no interf ...

What is the process for importing JSON from an NPM package in Angular version 15?

I've been dealing with a local package that contains a json file, and my current challenge is to load this json file into the Angular 15 app.component.ts. To bring the json file package into my Angular project, I followed this installation process: n ...

The date selected in matDatepicker does not match the formControl in Angular 8 when using Reactive Forms

https://i.stack.imgur.com/aHSyM.pngI am facing an issue with matDatepicker in Angular. The date retrieved from the API to populate my formControl using Reactive Forms is not matching the value displayed in matDatepicker. While the matDatePicker shows "12/3 ...

Upon launching Google Chrome, a series of errors plague the WSL2 Ubuntu setup while running Karma and Jasmine for Angular testing

My Angular project utilizes Google Chrome for testing with Karma & Jasmine, but upon starting Chrome, I encounter multiple errors. Despite trying various solutions found on Stack Overflow, none have been successful. The versions in use are: Chrome 99.0.4 ...

Triggering an event in Angular 2 after ngFor loop completes

I am currently attempting to utilize a jQuery plugin that replaces the default scrollbar within dynamic elements in Angular 2. These elements are generated using an ngFor loop, making it impossible to directly attach jQuery events to them. At some point, ...

Error message: Typescript class unable to access methods because of undefined properties (TypeError: Cannot read properties of undefined (reading 'method_name'))

During the compilation process, everything goes smoothly with no errors. However, when I try to call the method in the controller, an error occurs. It seems that some class methods are missing during the compilation phase. If you require more information ...

Angular: The specified function name cannot be called

My approach involves assigning an imported function to a component property within the constructor so that it can be utilized in the template. Although builds are successful, an error appears in my editor (Visual Studio Code with Angular Language Service) ...

Angular 5 Dilemma: Exporting UI Components without Locating Template

My current project involves developing UI Components that will be used in various web projects within the company. Our plan is to publish these UI components as an npm package on our local repository, and so far, the publishing process has been successful. ...

Attempting to reach MdTabBody within the Angular Material 2 framework

I am attempting to access the origin and position properties of the MdTabBody objects that have been created by using the following code snippet: @ViewChildren(MdTabBody) tabbodies: QueryList<MdTabBody>; My goal is to have control over the sliding ...

Ways to recycle code in two related functions in C#?

In my code, I have two classes with similar methods defined within them. These methods are almost identical, except for the fact that each one calls a different method. I would like to simplify this by using just one method in an abstract class. However, ...

Having trouble using Selenium for Chrome to locate an element in an HTML file using C#?

I am currently utilizing Selenium to locate elements within an HTML document that is being displayed in the Chrome browser. Unfortunately, the HTML code is not created by me and appears to have numerous issues. I do not have the ability to modify how it is ...

What is the best way to convert a dynamic JSON into a string?

After verifying the following Json as valid on www.jsonlint.com, I found that it is too large to display in its entirety. Below is a snippet: { "data": [{ "name": "Michael Jackson", "pic_large": "https://scontent.x.fbcdn.net/v/t1.0-1/p ...