0

I'm trying to grab some HD images from urls, resize them and upload to storage.

So far, i've gotten the image, and resized using sharp. The output API of sharp uses .toFile('output.jpg') or .toBuffer(), and I'm not sure how to proceed from here. What would be the easiest way to output the image, and upload it to firebase storage?

My code so far:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

const request = require('request').defaults({ encoding: null });
const sharp = require('sharp');

exports.copyThumbnailImage = functions.firestore.document('users/{userId}/vocab/{vocabId}').onCreate((snapshot, context) => {
  // mock: copyThumbnailImage({ chosenImages: [{ googleThumbnailUrl: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQlC7Vnu9CuZlA-nTpW8TLPd8dAE456LCpeXoadUKHoxB7WEmM1rfahqsfr", mime: "image/jpeg", originalUrl: "https://storage.tenki.jp/storage/static-images/suppl/article/image/2/27/278/27810/1/large.jpg" }] }, { params: { userId: 'zYCw6DmcEiOS8Yk4QltYPikykwP2', vocabId: 1420970 } })
  const data = snapshot.data()
  const vocabId = context.params.vocabId
  const images = data.chosenImages

  const checkExistencePromises = []
  // Promises check if image ref already exists in firestore
  images.forEach(imageObj => {
    checkExistencePromises.push(db.collection('userImages').where('originalUrl', '==', imageObj.originalUrl).where('vocabId', '==', vocabId).get())
  })

  return Promise.all(checkExistencePromises)
    .then(snapshots => {
      const getImagePromises = []
      snapshots.forEach((snapshot, i) => {
        if (snapshot.empty) {
          // if image doesn't exist already, upload thumbnail to DB, add doc to userImages and add link to review
          const originalUrl = images[i].originalUrl
          getImagePromises.push(getImage(originalUrl))
        } else {
          // add link to review
        }
      })

      return Promise.all(getImagePromises)
    })
    .then(responses => {
      responses.forEach(response => {
        sharp(response).resize(200, 200).toBuffer()
        // ????????
      })
    })
    .then(() => {
    })
    .catch(error => {
      console.log(error)
    })
})

function getImage (url) {
  return new Promise((resolve, reject) => {
    request.get(url, (err, res, body) => {
      if (err) reject(err)
      resolve(body)
    })
  })
}
Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
HJo
  • 1,902
  • 1
  • 19
  • 30

1 Answers1

2

You can save it to the local file system (the local /tmp disk) and upload it to Cloud Storage from there.

Have a look at this official Cloud Functions sample: https://github.com/firebase/functions-samples/blob/master/convert-images/functions/index.js. (I copy below the code for future reference)

In particular, look at how they save a temporary file with

return spawn('convert', [tempLocalFile, tempLocalJPEGFile]);

and how they upload it with:

return bucket.upload(tempLocalJPEGFile, {destination: JPEGFilePath});

In your case, instead of calling spawn() you would call

.toFile(-theTemporaryFielName-)

Finally, have a look at Write temporary files from Google Cloud Function and Attach firebase cloud function or cache its data from cloud function call about the /tmp disk.

Code from the Cloud Function Sample as of 08/01/2018 (link above)

const functions = require('firebase-functions');
const mkdirp = require('mkdirp-promise');
const gcs = require('@google-cloud/storage')();
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');

// File extension for the created JPEG files.
const JPEG_EXTENSION = '.jpg';

/**
 * When an image is uploaded in the Storage bucket it is converted to JPEG automatically using
 * ImageMagick.
 */
exports.imageToJPG = functions.storage.object().onFinalize((object) => {
  const filePath = object.name;
  const baseFileName = path.basename(filePath, path.extname(filePath));
  const fileDir = path.dirname(filePath);
  const JPEGFilePath = path.normalize(path.format({dir: fileDir, name: baseFileName, ext: JPEG_EXTENSION}));
  const tempLocalFile = path.join(os.tmpdir(), filePath);
  const tempLocalDir = path.dirname(tempLocalFile);
  const tempLocalJPEGFile = path.join(os.tmpdir(), JPEGFilePath);

  // Exit if this is triggered on a file that is not an image.
  if (!object.contentType.startsWith('image/')) {
    console.log('This is not an image.');
    return null;
  }

  // Exit if the image is already a JPEG.
  if (object.contentType.startsWith('image/jpeg')) {
    console.log('Already a JPEG.');
    return null;
  }

  const bucket = gcs.bucket(object.bucket);
  // Create the temp directory where the storage file will be downloaded.
  return mkdirp(tempLocalDir).then(() => {
    // Download file from bucket.
    return bucket.file(filePath).download({destination: tempLocalFile});
  }).then(() => {
    console.log('The file has been downloaded to', tempLocalFile);
    // Convert the image to JPEG using ImageMagick.
    return spawn('convert', [tempLocalFile, tempLocalJPEGFile]);
  }).then(() => {
    console.log('JPEG image created at', tempLocalJPEGFile);
    // Uploading the JPEG image.
    return bucket.upload(tempLocalJPEGFile, {destination: JPEGFilePath});
  }).then(() => {
    console.log('JPEG image uploaded to Storage at', JPEGFilePath);
    // Once the image has been converted delete the local files to free up disk space.
    fs.unlinkSync(tempLocalJPEGFile);
    fs.unlinkSync(tempLocalFile);
    return;
  });
});
Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
  • 1
    Thanks. I used `require('os').tmpdir()` to get the temp folder path, and then wrote the file to the db with `functions.storage().bucket('APPNAME.appspot.com').upload()` – HJo Jan 08 '19 at 13:23