Home > Net >  Transfer response from remote server to content script (Chrome extension)
Transfer response from remote server to content script (Chrome extension)

Time:11-09

What I want to achieve: Upon clicking icon of my Chrome extension, a file with downloaded from remote location and the user is presented with saveAs dialog.

Caveats: I'm using latest manifest from Google, namely v3.

I have edited this post many times as I I was able to achieve more and more. I leave only my latest code.

tl;dr Now almost everything works.Crucial thing is missing: response from server (body) is not saved. Instead string [object Object] is saved.

// when icon is clicked
chrome.action.onClicked.addListener(tab => {
  if (tab.url.startsWith('http')) {
    post({url: tab.url})
    .then(async res => ({
      filename: getFilename(res.headers.get('Content-Disposition')),
      blob: await res.blob()
    }))
    .then(response => {
      console.log(response);
      chrome.scripting.executeScript({
        target: {tabId: tab.id},
        func: saveFile,
        args: [response.filename, response.blob],
      })
    })
    .catch((error) => chrome.scripting.executeScript({
      target: {tabId: tab.id},
      func: showAlert,
      args: [error],
    }));
  }
});

function getFilename(header) {
  return /filename="(. )"/.exec(header)[1];
}

async function post(data = {}) {
    return await fetch('http://localhost:5000/citation',{
        method: 'POST',
        body: new URLSearchParams(data)
    });
}

function showAlert(error) {
    let message = error.error !== null ? error.error : error.message;
    alert("Error: "   message);
}

async function saveFile(filename, blob) {
  let link = document.createElement('a');

  let url = window.URL.createObjectURL(new Blob([blob],
      {type: 'application/octet-stream'}));
  link.href = url;
  link.download = filename;
  link.click();

  // For Firefox it is necessary to delay revoking the ObjectURL.
  setTimeout(() => {
    window.URL.revokeObjectURL(url);
    }, 250);
}

CodePudding user response:

The problem is in serializing the blob object from the chrome extension to the content script, which I think it's a bug in chrome. if you try to log the blob object that saveFile() receives, you will notice it's an empty object.

So instead of passing the blob object, you have to pass another serializable object to the content script.

I have converted the blob object to a base64 object then passed it to the content script.

First, let's create a function to convert the blob object to a base64 object

function blobToBase64(blob) {
    return new Promise((resolve, _) => {
        const reader = new FileReader();
        reader.onloadend = () => resolve(reader.result);
        reader.readAsDataURL(blob);
    });
}

Then convert the blob object

chrome.action.onClicked.addListener(tab => {
  if (tab.url.startsWith('http')) {
    post({url: tab.url})
    .then(async res => ({
      filename: getFilename(res.headers.get('Content-Disposition')),
      blob: await res.blob()
    }))
    .then(async response => {
      var base64 = await blobToBase64(response.blob)
      chrome.scripting.executeScript({
        target: {tabId: tab.id},
        func: saveFile,
        args: [response.filename, base64],
      })
    })
    .catch((error) => chrome.scripting.executeScript({
      target: {tabId: tab.id},
      func: showAlert,
      args: [error],
    }));
  }
});

then in the saveFile() method, you need to convert that base64 into a file

async function saveFile(filename, base64URL) {
    fetch(base64URL)
        .then(res => res.blob())
        .then(blob => {
            let link = document.createElement('a');

            let url = window.URL.createObjectURL(new Blob([blob],
                { type: 'application/octet-stream' }));
            link.href = url;
            link.download = filename;
            link.click();

            // For Firefox it is necessary to delay revoking the ObjectURL.
            setTimeout(() => {
                window.URL.revokeObjectURL(url);
            }, 250);
        })
}

If you will not do anything in saveFile() but download the file, then it will be better to download the file from the extension itself because converting the blob to base64 then getting it back to blob again is slowing down the downloading process.

  • Related