Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send a blob/Uint8Array to the offscreen document e.g. to use URL.createObjectURL?

Because of the size of my generated PDF, the base64 conversion is not possible and I have to use the offscreen functionality.

Based on this answer -> https://stackoverflow.com/a/75539867/8016254

I tried to download a PDF file that will be reated in my custom chrome extension.

background.js

import './pdf-lib.min.js';

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    (async () => {
        if (message.pdfUrls && message.pdfUrls.length > 0) {
            // Debug PDF
            const urls = ["https://www.africau.edu/images/default/sample.pdf"];
            await mergeAllPDFs(urls, "Test");
        }
    })();

    return true;
});
async function mergeAllPDFs(urls, filename) {
    const numDocs = urls.length;
    const pdfDoc = await PDFLib.PDFDocument.create();
    for (let i = 0; i < numDocs; i++) {
        console.log(`Fetching ${urls[i]}`)
        const donorPdf = await fetch(urls[i]);
        const donorPdfBytes = await donorPdf.arrayBuffer();
        const donorPdfDoc = await PDFLib.PDFDocument.load(donorPdfBytes);
        const copiedPages = await pdfDoc.copyPages(donorPdfDoc, donorPdfDoc.getPageIndices());
        copiedPages.forEach((page) => pdfDoc.addPage(page));
    }
    const pdfDocBytes = await pdfDoc.save();
    const waiting = await chrome.offscreen.createDocument({
        url: 'offscreen.html',
        reasons: ['BLOBS'],
        justification: 'reason for needing the document',
    });
    chrome.runtime.sendMessage({
        data: pdfDocBytes
    }, (response) => {
        console.log(response);
        const url = response.url;
        console.log(url);
        chrome.downloads.download({
            url: url,
            filename: filename + ".pdf"
        });
    });

}

offscreen.js

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    const blob = new Blob([message.data], { type: "application/pdf" });
    const url = URL.createObjectURL(blob);
    sendResponse({ url: url });
    return true;
});

The resulting PDF seems to be corrupt and way smaller than expected. Any ideas?

like image 769
ExceptionFinder Avatar asked Oct 18 '25 19:10

ExceptionFinder


1 Answers

Problem: chrome.runtime.sendMessage can't send binary data in Chrome.

Solution: use standard web messaging.

// background.js

async function main() {
  const blob = new Blob(['foo']);
  const blobUrl = await getBlobUrl(blob);
  chrome.downloads.download({url: blobUrl, filename: filename + '.pdf'});
}
async function getBlobUrl(blob) {
  const url = chrome.runtime.getURL('offscreen.html');
  try {
    await chrome.offscreen.createDocument({
      url,
      reasons: ['BLOBS'],
      justification: 'MV3 requirement',
    });
  } catch (err) {
    if (!err.message.startsWith('Only a single offscreen')) throw err;
  }
  const client = (await clients.matchAll({includeUncontrolled: true}))
    .find(c => c.url === url);
  const mc = new MessageChannel();
  client.postMessage(blob, [mc.port2]);
  const res = await new Promise(cb => (mc.port1.onmessage = cb));
  return res.data;
}

// offscreen.html

<script src="offscreen.js"></script>

// offscreen.js

let timeout;
navigator.serviceWorker.onmessage = e => {
  clearTimeout(timeout);
  e.ports[0].postMessage(URL.createObjectURL(e.data));
  timeout = setTimeout(close, 60e3);
};
// Note that if you use navigator.serviceWorker.addEventListener instead of `onmessage`,
// you may also need to call navigator.serviceWorker.startMessages() afterwards,
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/startMessages

Don't forget to add "offscreen" to "permissions" in manifest.json.

like image 190
wOxxOm Avatar answered Oct 21 '25 14:10

wOxxOm



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!