Skip to content

Instantly share code, notes, and snippets.

@mflisikowski
Created February 21, 2018 22:12
Show Gist options
  • Save mflisikowski/621b05e18900e1a81e7413a64f4e118d to your computer and use it in GitHub Desktop.
Save mflisikowski/621b05e18900e1a81e7413a64f4e118d to your computer and use it in GitHub Desktop.
Firebase Cloud Functions: Storage Thumbnails
'use strict'
const functions = require('firebase-functions')
const mkdirp = require('mkdirp-promise')
const gcs = require('@google-cloud/storage')({
keyFilename: '***********************************-c3d9469093.json'
})
const admin = require('firebase-admin')
const spawn = require('child-process-promise').spawn
const path = require('path')
const os = require('os')
const fs = require('fs')
admin.initializeApp(functions.config().firebase)
const THUMB_1200_WIDTH = 1200
const THUMB_600_HEIGHT = 600
const THUMB_600_WIDTH = 600
const THUMB_400_HEIGHT = 400
const THUMB_200_WIDTH = 200
const THUMB_200_HEIGHT = 200
const THUMB_LARGE_PREFIX = 'thumb_large_'
const THUMB_SMALL_PREFIX = 'thumb_small_'
const THUMB_ICON_PREFIX = 'thumb_icon_'
exports.storageThumbnails2 = functions.storage.object()
.onChange((event) => {
const filePath = event.data.name
const contentType = event.data.contentType
const fileDir = path.dirname(filePath)
const fileName = path.basename(filePath)
const thumbLargePath = path.normalize(path.join(fileDir, `${THUMB_LARGE_PREFIX}${fileName}`))
const thumbSmallPath = path.normalize(path.join(fileDir, `${THUMB_SMALL_PREFIX}${fileName}`))
const thumbIconPath = path.normalize(path.join(fileDir, `${THUMB_ICON_PREFIX}${fileName}`))
const tempLocalFile = path.join(os.tmpdir(), filePath)
const tempLocalDir = path.dirname(tempLocalFile)
const tempLocalThumbLarge = path.join(os.tmpdir(), thumbLargePath)
const tempLocalThumbSmall = path.join(os.tmpdir(), thumbSmallPath)
const tempLocalThumbIcon = path.join(os.tmpdir(), thumbIconPath)
// Exit if this is a move or deletion event.
if (event.data.resourceState === 'not_exists') {
console.log('This is a deletion event.')
return null
}
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
console.log('This is not an image.')
return null
}
// Exit if the image is already a thumbnail.
if (
fileName.startsWith(THUMB_LARGE_PREFIX) ||
fileName.startsWith(THUMB_SMALL_PREFIX) ||
fileName.startsWith(THUMB_ICON_PREFIX)) {
console.log('Already created!')
return null
}
// Cloud Storage files.
const bucket = gcs.bucket(event.data.bucket)
const file = bucket.file(filePath)
const thumbLargeFile = bucket.file(thumbLargePath)
const thumbSmallFile = bucket.file(thumbSmallPath)
const thumbIconFile = bucket.file(thumbIconPath)
const metadata = {
contentType: contentType
}
return mkdirp(tempLocalDir)
.then(() => file.download({ destination: tempLocalFile }))
// Large thumb
.then(() => { console.log('The file has been downloaded to', tempLocalFile)
return spawn(
`convert`, [
`-define`,
`jpeg:size=${THUMB_1200_WIDTH}x${THUMB_600_HEIGHT}>`,
tempLocalFile,
`-thumbnail`,
`${THUMB_1200_WIDTH}x${THUMB_600_HEIGHT}>^`,
`-gravity`,
`center`,
`-extent`,
`${THUMB_1200_WIDTH}x${THUMB_600_HEIGHT}>`,
tempLocalThumbLarge
]
)
})
.then(() => { console.log('Thumbnail created at', tempLocalThumbLarge)
// Uploading the Thumbnail.
return bucket.upload(tempLocalThumbLarge, {
destination: thumbLargePath,
metadata: metadata
})
})
// Small thumb
.then(() => { console.log('The file has been downloaded to', tempLocalFile)
return spawn(
`convert`, [
`-define`,
`jpeg:size=${THUMB_600_WIDTH}x${THUMB_400_HEIGHT}>`,
tempLocalFile,
`-thumbnail`,
`${THUMB_600_WIDTH}x${THUMB_400_HEIGHT}>^`,
`-gravity`,
`center`,
`-extent`,
`${THUMB_600_WIDTH}x${THUMB_400_HEIGHT}>`,
tempLocalThumbSmall
]
)
})
.then(() => { console.log('Thumbnail created at', tempLocalThumbSmall)
// Uploading the Thumbnail.
return bucket.upload(tempLocalThumbSmall, {
destination: thumbSmallPath,
metadata: metadata
})
})
// Icon thumb
.then(() => { console.log('The file has been downloaded to', tempLocalFile)
return spawn(
`convert`, [
`-define`,
`jpeg:size=${THUMB_200_WIDTH}x${THUMB_200_HEIGHT}>`,
tempLocalFile,
`-thumbnail`,
`${THUMB_200_WIDTH}x${THUMB_200_HEIGHT}>^`,
`-gravity`,
`center`,
`-extent`,
`${THUMB_200_WIDTH}x${THUMB_200_HEIGHT}>`,
tempLocalThumbIcon
]
)
})
.then(() => { console.log('Thumbnail created at', tempLocalThumbIcon)
// Uploading the Thumbnail.
return bucket.upload(tempLocalThumbIcon, {
destination: thumbIconPath,
metadata: metadata
})
})
// Finish upload
.then(() => {
console.log('Thumbnails uploaded to Storage')
// Once the image has been uploaded delete the local files to free up disk space.
fs.unlinkSync(tempLocalFile)
fs.unlinkSync(tempLocalThumbLarge)
fs.unlinkSync(tempLocalThumbSmall)
fs.unlinkSync(tempLocalThumbIcon)
// Get the Signed URLs for the thumbnail and original image.
const config = {
action: 'read',
expires: '03-01-2500',
}
return Promise.all([
thumbLargeFile.getSignedUrl(config),
thumbSmallFile.getSignedUrl(config),
thumbIconFile.getSignedUrl(config),
file.getSignedUrl(config)
])
})
.then((results) => {
console.log('Got Signed URLs.')
const thumbLargeResult = results[0]
const thumbSmallResult = results[1]
const thumbIconResult = results[2]
const originalResult = results[3]
const thumbLargeFileUrl = thumbLargeResult[0]
const thumbSmallFileUrl = thumbSmallResult[0]
const thumbIconFileUrl = thumbIconResult[0]
const fileUrl = originalResult[0]
// Add the URLs to the Database
return admin.database().ref('images').push({
oryginal: {
name: fileName,
url: fileUrl
},
large: {
name: `${THUMB_LARGE_PREFIX}${fileName}`,
url: thumbLargeFileUrl
},
small: {
name: `${THUMB_SMALL_PREFIX}${fileName}`,
url: thumbSmallFileUrl
},
icon: {
name: `${THUMB_ICON_PREFIX}${fileName}`,
url: thumbIconFileUrl
}
})
})
.then(() => console.log('Thumbnail URLs saved to database.'))
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment