0

I created a Chrome Extension that takes a screenshot of the current tab. I'd like to then upload the image to Firebase.

chrome.tabs.captureVisibleTab returns at dataUrl string [docs], and I attempt to include it as formData to a POST request as follows:

screenshotBtn.onclick = function(){
  // generate the screenshot
  chrome.tabs.captureVisibleTab(null, { format: 'png', quality: 80 }, function(dataUrl){
    // console.log(dataUrl);

    // create imageBlob from data
    let imgBlob = b64toBlob(dataUrl.replace('data:image/png;base64,', ''), "image/png");
    console.log('imgBlob: ', imgBlob);
    
    let formData = new FormData();
    formData.append('image', imgBlob);

    // upload to Firebase Storage
    let url = endpoint + '/uploadImage';
    fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': false
      },
      body: formData
    })
    .then((response) => response.json())
    .then(data => {
      console.log(data);
    });
  });
};

(the function b64toBlob is taken from this Stackoverflow Post)

I try to process the image and upload it to Firebase on my server as follows:

app.post("/api/uploadImage", (req, res) => {
    (async () => {
        try {
            console.log(req.body.image);
            // req.body.image = image in base64 format
            await uploadFile(req.body.image);
            return res.status(200).send();
        } catch(error){
            console.log(error);
            return res.status(500).send(error);
        }
    })();
});

async function uploadFile(imageBlob){
    const metadata = {
        metadata: {
            firebaseStorageDownloadTokens: uuid()
        },
        contentType: 'image/jpeg',
        cacheControl: 'public, max-age=31536000'
    };

    await bucket.upload(imageBlob, {
        gzip: true,
        metadata: metadata,
    });
}

I'm having trouble telling whether the issue is

  1. with the way the POST request is formatted
  2. with the way the file is received on the server

I am currently getting a 500 error, and this is what the request looks like: enter image description here

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
scientiffic
  • 9,045
  • 18
  • 76
  • 149
  • There might be chances that `req.body` couldn't handle binary data. So either 1. you add an Express extension for handling binary data, or 2. instead of sending binary data, you could send the base64 data and convert back to blob in the server-side. – Michael Oct 06 '20 at 05:25

1 Answers1

0

I was able to piece together a working solution.

For submitting the POST request on the client:

screenshotBtn.onclick = function(){
  // generate the screenshot
  chrome.tabs.captureVisibleTab(null, { format: 'png', quality: 80 }, function(dataUrl){

    let body = {
      "image" : dataUrl
    };

    // upload to Firebase Storage
    let url = endpoint + '/uploadImage';
    fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(body)
    })
    .then((response) => response.json())
    .then(data => {
      console.log(data);
    });
  });
};

On the server:

// upload to storage
app.post("/api/uploadImage", (req, res) => {
    let image = req.body.image;
    let base64EncodedImageString = image.replace('data:image/png;base64,', '');
    let imageBuffer = new Buffer.from(base64EncodedImageString, 'base64');    
    let file = bucket.file("test-image.png");

    file.save(imageBuffer, {
        metadata: { 
            metadata: {
                firebaseStorageDownloadTokens: uuid
            },
        contentType: 'image/png',
        cacheControl: 'public, max-age=31536000',
        public: true,
        validation: 'md5'
        }
    }, (error) => {
        if(error){
            res.status(500).send(error);
        }
        return res.status(200).send('finished uploading');
    });
});

where bucket is admin.storage().bucket() (and the admin is an instance of firebase-admin that has been properly initialized with my credentials)

Note that unless the uuid is provided, the image is listed as having been uploaded in Firebase Storage, but you can't actually view it or download it (you just see a spinning loading placeholder).

These posts were particularly helpful for coming to this solution:

https://stackoverflow.com/a/46999645/1720985 https://github.com/firebase/firebase-admin-node/issues/694#issuecomment-583141427

scientiffic
  • 9,045
  • 18
  • 76
  • 149