Skip to content

Commit 2c7a439

Browse files
committed
Image-sharp sample added
1 parent a122f19 commit 2c7a439

File tree

5 files changed

+162
-0
lines changed

5 files changed

+162
-0
lines changed

Diff for: README.md

+4
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ Uses an HTTP trigger.
197197
This sample demonstrates how to send a Firebase Cloud Messaging (FCM) notification to the developer device each time your app get a new user or lose one.
198198
Uses an Analytics trigger.
199199

200+
### [Thumbnail creating using Node.js Stream & Sharp](/image-sharp)
201+
202+
This sample shows how to use Node.js Stream to read image from Cloud Storage, generate a thumbnail image using Sharp and upload it back to Cloud Storage.
203+
200204
## Contributing
201205

202206
We'd love that you contribute to the project. Before doing so please read our [Contributor guide](CONTRIBUTING.md).

Diff for: image-sharp/README.md

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Image resizing using Node.js Stream and Sharp - Cloud Storage trigger
2+
3+
This quickstart demonstrates using **Firebase SDK for Cloud Functions** setup with a Cloud Storage trigger.
4+
5+
## Introduction
6+
7+
This sample automatically generates thumbnails for images that are uploaded to Cloud Storage.
8+
This is a alternative approach for using ImageMagick, see [quickstarts/thumbnail](../quickstarts/thumbnail).
9+
10+
11+
## Functions Code
12+
13+
See file [functions/index.js](functions/index.js) for the thumbnail generation code.
14+
15+
The thumbnail generation is performed using [Sharp](http://sharp.dimens.io/en/stable/).
16+
Download, resize input/output and upload uses Node.js [Stream](https://nodejs.org/api/stream.html)
17+
18+
The dependencies are listed in [functions/package.json](functions/package.json).
19+
20+
21+
## Trigger rules
22+
23+
The function triggers on upload of any file to your Firebase project's default Cloud Storage bucket.
24+
25+
26+
## Deploy and test
27+
28+
To deploy and test the sample:
29+
30+
- Create a Firebase project on the [Firebase Console](https://console.firebase.google.com) and visit the **Storage** tab.
31+
- Get the code, for instance using `git clone https://github.com/firebase/functions-samples`
32+
- Enter the correct directory `cd functions-samples/image-sharp`
33+
- Setup the CLI to use your Firebase project using `firebase use --add` and select your Firebase project
34+
- Deploy your project's code using `firebase deploy`
35+
- Go to the Firebase Console **Storage** tab and upload an image. After a short time a thumbnail image with the same name but a `thumb_` prefix will be created in the same folder (make sure you refresh the UI to see the new file).
36+
37+
38+
## Contributing
39+
40+
We'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).
41+
42+
43+
## License
44+
45+
© Google, 2016. Licensed under an [Apache-2](../../LICENSE) license.

Diff for: image-sharp/firebase.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

Diff for: image-sharp/functions/index.js

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* Copyright 2016 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for t`he specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
'use strict';
17+
18+
// [START import]
19+
const functions = require('firebase-functions');
20+
const gcs = require('@google-cloud/storage')();
21+
const sharp = require('sharp');
22+
// [END import]
23+
24+
const THUMB_MAX_WIDTH = 200;
25+
const THUMB_MAX_HEIGHT = 200;
26+
27+
// [START generateThumbnail]
28+
/**
29+
* When an image is uploaded in the Storage bucket We generate a thumbnail automatically using
30+
* ImageMagick.
31+
*/
32+
// [START generateThumbnailTrigger]
33+
exports.generateThumbnail = functions.storage.object().onChange(event => {
34+
// [END generateThumbnailTrigger]
35+
// [START eventAttributes]
36+
const object = event.data; // The Storage object.
37+
38+
const fileBucket = object.bucket; // The Storage bucket that contains the file.
39+
const filePath = object.name; // File path in the bucket.
40+
const contentType = object.contentType; // File content type.
41+
const resourceState = object.resourceState; // The resourceState is 'exists' or 'not_exists' (for file/folder deletions).
42+
const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.
43+
// [END eventAttributes]
44+
45+
// [START stopConditions]
46+
// Exit if this is triggered on a file that is not an image.
47+
if (!contentType.startsWith('image/')) {
48+
console.log('This is not an image.');
49+
return;
50+
}
51+
52+
// Get the file name.
53+
const fileName = filePath.split('/').pop();
54+
// Exit if the image is already a thumbnail.
55+
if (fileName.startsWith('thumb_')) {
56+
console.log('Already a Thumbnail.');
57+
return;
58+
}
59+
60+
// Exit if this is a move or deletion event.
61+
if (resourceState === 'not_exists') {
62+
console.log('This is a deletion event.');
63+
return;
64+
}
65+
66+
// Exit if file exists but is not new and is only being triggered
67+
// because of a metadata change.
68+
if (resourceState === 'exists' && metageneration > 1) {
69+
console.log('This is a metadata change event.');
70+
return;
71+
}
72+
// [END stopConditions]
73+
74+
// [START thumbnailGeneration]
75+
// Download file from bucket.
76+
const bucket = gcs.bucket(fileBucket);
77+
78+
const metadata = {
79+
contentType: contentType
80+
};
81+
// We add a 'thumb_' prefix to thumbnails file name. That's where we'll upload the thumbnail
82+
const thumbFilePath = filePath.replace(/(\/)?([^\/]*)$/, '$1thumb_$2');
83+
// Create write stream for uploading thumbnail
84+
const thumbnailUploadStream = bucket.file(thumbFilePath).createWriteStream({metadata});
85+
86+
// Create Sharp pipeline for resizing the image and use pipe to read from bucket read stream
87+
const pipeline = sharp();
88+
pipeline
89+
.resize(THUMB_MAX_WIDTH, THUMB_MAX_HEIGHT)
90+
.max()
91+
.pipe(thumbnailUploadStream);
92+
93+
bucket.file(filePath).createReadStream().pipe(pipeline);
94+
95+
const streamAsPromise = new Promise((resolve, reject) =>
96+
thumbnailUploadStream.on('finish', resolve).on('error', reject));
97+
return streamAsPromise.then(() => {
98+
console.log('Thumbnail created successfully');
99+
});
100+
// [END thumbnailGeneration]
101+
});
102+
// [END generateThumbnail]

Diff for: image-sharp/functions/package.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "generate-thumbnail-functions",
3+
"description": "Generate Thumbnail Firebase Functions sample",
4+
"dependencies": {
5+
"@google-cloud/storage": "^0.4.0",
6+
"firebase-admin": "^4.1.1",
7+
"firebase-functions": "^0.5.1",
8+
"sharp": "^0.18.1"
9+
}
10+
}

0 commit comments

Comments
 (0)