Search code examples
reactjspdfblobuint8array

Displaying a pdf in a new browser tab from a Uint8Array


Background

I'm generating a PDF file dynamically in the backend of my web app using pdf-lib. Then, I send the PDF bytes in the form of Uint8Array to frontend in order to open a new tab that will display the file outside of the app's frontend.

The Problem

While the request is successfully completed with the desired Uint8Array payload, there is an error alert in the new tab (Chrome) that says: Error Failed to load PDF document.

The Uint8Array data is inside the response body:

{
    success: true,
    data: {0: 37, 1: 80, 2: 68, 3: 70, 4: 45, ...}
}

What I tried

  1. Before returning the bytes to the frontend I am able to save and access the PDF file on my PC using the fs.readFileSync() method.

  2. I tried using both Chrome and Edge, but the problem persists.

Backend code

try {
        const pdfBytes = await pdfDoc.save()    // .save() method returns Promise<Uint8Array>
        const filePath = './dddd.pdf'
        fs.writeFileSync(filePath, pdfBytes)    // This line creates a file to PC successfully
        return pdfBytes
    } catch (err) {
        console.log('erroe while saving');
        console.log(err);
    }

Frontend Code

if (result.success) {
       const pdfBlob = new Blob([result.data], { type: 'application/pdf' })
       const pdfURL = URL.createObjectURL(pdfBlob)
       console.log(pdfURL);   // -->  blob:http://localhost:3000/8f168e27-934e-4d80-a479-31975e22eaa1
            
       window.open(pdfURL, '_blank')
 }

Solution

  • As it seems the problem is that I cant directly use the .json() or JSON.stringefy() methods on an Uint8Array and just send it to the frontend because it corrupts the binary data.

    Solution

    In the backend, I first had to convert the Uint8Array to a base64string, send the string in a json to the front end, convert the base64string back to Uint8Array and only then create the blob, create a temporary URL and open it in a new tab.

    Backend code:

    import { NextRequest, NextResponse } from "next/server";
    import { generatePdf } from "@/lib/pdf/generate/generatePDF";
    
    export async function POST(req: NextRequest, res: NextResponse , context: {}) {
        try {
            console.log(' request reached!!!');
            
            const body = await req.json()
            const pdfBytes = await generatePdf(body)
            const base64PDF = Buffer.from(pdfBytes as Uint8Array).toString('base64')
            return NextResponse.json({success: true, data: base64PDF}, { status: 200 })
        } catch (err) {
            console.log('Error occurred while ...');
            console.log(err);
            return NextResponse.json({success: false}, { status: 500 })
        }
    }
    

    Frontend code

    if (result.success) {
                const base64PDF = result.data;
                const pdfBytes = new Uint8Array(atob(base64PDF).split('').map(char => char.charCodeAt(0)));
                const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' });
                const pdfURL = URL.createObjectURL(pdfBlob);
                window.open(pdfURL, '_blank');
    }