Generating and saving a PDF file using a binary string in JavaScript or TypeScript

A server response to an Axios request contains the content of a PDF as a binary string.

export const fetchPDFfile = async (id: string): Promise<string> => {
  const { data } = await http.get<string>(`${baseUrl}/${id}.pdf`);
  return data;
};

When viewed in Chrome devtools or logged in the console, the data appears as follows:

%PDF-1.4
%âãÏÓ
2 0 obj <</ColorSpa ......
..........
startxref
10991
%%EOF
  • Is defining string as the expected type of response body from Axios correct, or should it be (cast to) Blob?

Now, I aim to download this as a PDF file on the client-side. Despite numerous attempts and unanswered questions, I have not been successful.

My current approach (in a React component) involves:

    const data = await fetchPDFfile(props.id);
    const blob = new Blob([data], { type: 'application/pdf' });
    const href = window.URL.createObjectURL(blob);
    const theLink = document.createElement('a');
    theLink.href = href;
    theLink.download = props.id + '.pdf';
    document.body.appendChild(theLink);
    theLink.click();
    document.body.removeChild(theLink);

Downloading this results in a PDF file with 3 blank pages. The original document should be 3 pages long, but the pages are empty.

const href = window.URL.createObjectURL(data); // instead of blob
throws an error.

What is the correct way to convert and download this PDF file? Generally, is the above process necessary, or should I directly download it from the server (similar to what cordova-plugin-file-transfer does)?

Answer №1

Situation

If you wish for the file to be downloaded upon the user clicking the link.

Approach 1-

Simply insert the link within the <a> tag.

Drawback- In case of any errors, no error message can be displayed on the screen.

Thus, this leads to the alternative solution.

Approach 2-

Invoke the URL as an API to download the file upon receiving a success message. For this, I utilize File-server.js

**Remember to specify the {responseType: 'blob'}, while initiating the request

http.get<string>(`${baseUrl}/${id}.pdf`, {responseType: 'blob'})

as we do not desire a response with Content-Type: application/json

illustrative code:

import FileSaver from 'file-saver';

downloadPdf() {
    var blob = new Blob([data], {type: "application/pdf"});
    FileSaver.saveAs(blob, "filename");
}

Answer №2

To begin, utilize Blob as the generic argument for Promise.

Let's use the fetch API for easy testing.

fetch('https://www.jianjunchen.com/papers/CORS-USESEC18.slides.pdf').then(x => x.blob()).then(b => console.log(b.type))

This should output "application/pdf" if the file is indeed a PDF.

Converting non-PDF blobs to a PDF type can break the data. Similarly, converting a true string to a PDF Blob could result in an invalid PDF file.

To verify if b is indeed a blob, simply use console.log(b instanceof Blob) which should return true. If you have already received a blob, there is no need to create a new one using new Blob([data]).

The following example functions correctly:

fetch('https://www.jianjunchen.com/papers/CORS-USESEC18.slides.pdf').then(x => x.blob()).then(b => {
  const url = window.URL.createObjectURL(b);
  var a = document.createElement("a");
  document.body.appendChild(a);
  a.style = "display: none";
  a.href = url;
  a.download = "a.pdf";
  a.click();
  window.URL.revokeObjectURL(url);
})

Apologies for the formatting issues in the code, it was challenging to paste it correctly.

Answer №3

When using NestJS framework

Within the service:

async download() {
        try {
            const url = `https://example.com`;
            
            const headers = {
              Authorization: `Bearer ${this.accessToken}`,
              'Content-Type': 'application/pdf'
            };
            const response: AxiosResponse<any> = await this.httpService.get(url, { headers, responseType: 'arraybuffer' }).toPromise();
      
            return response.data;
        } catch (error) {
            throw new Error('Unable to download '+ error);
        }
    }

Inside the Controller:

@Get('download')
    async download(@Res() res) {
        const data = await this.appService.download();

        res.setHeader('Content-disposition', 'attachment; filename="file.pdf"');
        res.setHeader('Content-type', 'application/pdf');

        res.send(data);
    }

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

Effectively accessing the Templater object within a user script for an Obsidian QuickAdd plugin

I have developed a user script to be used within a QuickAdd plugin Macro in Obsidian. The Macro consists of a single step where the user script, written in JavaScript following the CommonJS standard, is executed. Within this script, there is a task to gene ...

In the past, my code would run smoothly without any issues, but now I am encountering a runtime error even though the code comp

Recently, I started learning TypeScript and encountered an issue while working with Classes. My code was functioning properly before but now it's displaying a runtime error. ...

`Adding data to Rails database and displaying without the need for reloading using AngularJS`

When working on my Rails project, I encountered an issue with adding data to the database using `http.post` from an AngularJS controller. Below is the code snippet demonstrating this: RestaurantIndexCtrl.js.coffee: restauranteur.controller 'Restaura ...

Trouble with styling the Ngx-org-chart in Angular

I integrated the ngx-org-chart library into my Angular project, but I'm facing issues with the styles not applying correctly. Here are the steps I followed: I first installed the package using: yarn add ngx-org-chart Then, I added the ngx-org ...

Angular is not programmed to automatically reflect updates made to my string array

let signalRServerEndPoint = 'https://localhost:44338'; this.connection = $.hubConnection(signalRServerEndPoint); this.proxy = this.connection.createHubProxy('MessagesHub'); this.proxy.on("ReceiveMessage", (message) => { ...

Retrieve the total count of tables within a specific div element

If I have an unspecified amount of tables within a div, how can I determine the total number of tables using either plain JavaScript or jQuery? ...

Using Node.js to display the outcome of an SQL query

I have been attempting to execute a select query from the database and display the results. However, although I can see the result in the console, it does not appear on the index page as expected. Additionally, there seems to be an issue with the way the r ...

Triggering this function when clicked

I am trying to figure out a way to trigger this code onClick instead of automatically. Can anyone help me with this question? Below is the JavaScript code snippet: $("input[type=radio],button").each(function () { var outputName = $(this).attr("d ...

Issue with React filter function where data is not being displayed when search query is left

I am facing an issue where the data does not show up when the search term is empty. Previously, I used to have this line in my code if (!searchTerm) return data for my old JSON data, and it worked fine. However, now that I'm using Sanity CDN, this lo ...

Several DIVs with the same class can have varying CSS values

I am looking to modify the left-margin value of various separate DIVs using JavaScript. The challenge is: I only want to use a single className, and I want the margin to increase by 100px for each instance of the class. This way, instead of all the DIVs ...

What is the best way to retrieve an element that has been altered in its state?

I encountered a scenario where I want an image to have a border when clicked, and if clicked again, the border should be removed. However, the border should also be removed if another image is clicked instead. I believe there are a couple of approaches to ...

Error: Provider not recognized

Currently, I am in the process of creating a basic chat application using express.js, socket.io, and angular. The functionality seems to be working properly. However, I am encountering an issue where the socket message event is not synchronizing and displa ...

Angular virtual scrolling not populating the list with data

I have encountered a challenge while trying to implement infinite virtual scroll on an Angular 7 project with a GraphQL backend from Hasura. I am puzzled by the fact that the new data is not being added and there are multiple API requests being made when ...

`Weaving mesh into place with three.js`

I'm struggling to grasp how to position my cubes on the canvas. I can't quite figure out how positioning actually works. I'm trying to find a way to determine if my mesh reaches the limits of the canvas. But what exactly is the unit of posit ...

Guide to inheriting functions from a parent component in Angular 2

Hello, I am a beginner in the realm of angular2 and would appreciate any assistance in refining my vocabulary and terminology. Currently, I have a class that consists of two directives structured as follows: In parent.component.ts, the Parent component i ...

Tips for adding two values simultaneously to an array in JavaScript

I am having trouble inserting two values into an array during each loop iteration. Here is the code I have tried: feature_arr = []; $form.find( '.variations .value' ).each( function() { var $radios = $( this ).fi ...

What is the best way to store and present data dynamically in D3.js?

Currently, I'm engaged in a project involving my Raspberry Pi that focuses on a sensor collecting data continuously. My objective is to save this data in a specific format such as SQL, CSV, JSON, or any other suitable option, and then present it visua ...

Is there a way to prevent a 'keyup' event from being triggered by a 'keydown' event?

I have a tool that resolves zip codes and I am currently utilizing a keyup event handler to trigger a server query once the input length reaches 5 characters. However, I want to prevent unnecessary calls to the script, so I am exploring the possibility o ...

Error: Module not found - Imported Node Module

I have been struggling to make modifications to an npm package with little success. I have spent hours troubleshooting and would greatly appreciate some guidance. https://www.npmjs.com/package/dxf After forking the package into my Github account, I proce ...

HTML5 enables users to pick their preferred font style

In my JavaScript and HTML5 course, I am working on developing a website where users can choose the background color and decide between using SANS SERIF or SANS fonts. The background color selection feature is already functioning successfully -- var inputC ...