Skip to content

Commit 1ee0309

Browse files
feat(dart): add resize-image quickstart sample (#1272)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent bcbc707 commit 1ee0309

File tree

5 files changed

+175
-0
lines changed

5 files changed

+175
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
**/.firebase
44
**/.firebaserc
55
**/.runtimeconfig.json
6+
**/functions.yaml
7+
**/.env.local
68
*/npm-debug.log
79
lerna-debug.log
810
*~
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.dart_tool/
2+
.packages
3+
build/
4+
*.dart_tool
5+
pubspec.lock
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import 'dart:typed_data';
2+
3+
import 'package:google_cloud_storage/google_cloud_storage.dart'
4+
show ObjectMetadata, NotFoundException;
5+
import 'package:firebase_functions/firebase_functions.dart';
6+
import 'package:image/image.dart';
7+
8+
final defaultWidth = defineInt(
9+
'DEFAULT_WIDTH',
10+
ParamOptions<int>(defaultValue: 300),
11+
);
12+
13+
void main(List<String> args) async {
14+
await fireUp(args, (firebase) {
15+
/// An https function that resizes images in Cloud Storage.
16+
/// It creates a separate Storage folder to cache stored images
17+
/// so that it does not need to resize an image twice.
18+
///
19+
/// It returns an HTTP redirect to the public Storage download URL.
20+
///
21+
/// The query params it accepts are:
22+
/// - image: the image file path in Cloud Storage
23+
/// - width (optional): the width in pixels to resize to
24+
///
25+
/// Example call: https://<function-url>?image=myFile.png&width=400
26+
firebase.https.onRequest(name: 'imageOptimizer', (request) async {
27+
// Parse arguments from query params in the URL
28+
final queryParams = request.url.queryParameters;
29+
final imageFileName = queryParams['image'];
30+
if (imageFileName == null) {
31+
throw InvalidArgumentError(
32+
'No image provided. Include the image file name as a query param.',
33+
);
34+
}
35+
var width = int.tryParse(queryParams['width'] ?? "");
36+
if (width == null) {
37+
logger.info(
38+
'Cloud not parse width from query params. Using default width.',
39+
);
40+
width = defaultWidth.value();
41+
}
42+
43+
// Get the storage bucket from the built-in parameter
44+
// https://firebase.google.com/docs/functions/config-env#built-in-parameters
45+
final bucketName = storageBucket.value();
46+
final bucket = firebase.adminApp.storage().bucket(bucketName);
47+
48+
// Return early if the image has been resized before
49+
final cachedFileName = 'image-optimizer-cache/${width}w-${imageFileName}';
50+
try {
51+
await bucket.storage.objectMetadata(bucketName, cachedFileName);
52+
final downloadUrl = await firebase.adminApp.storage().getDownloadURL(
53+
bucket,
54+
cachedFileName,
55+
);
56+
logger.log('Cache hit. Using existing resized image.');
57+
return Response.movedPermanently(downloadUrl);
58+
} on NotFoundException {
59+
logger.log('Cache miss. Resizing image to width ${width}');
60+
}
61+
62+
// Download original image
63+
List<int> originalBytes;
64+
try {
65+
originalBytes = await bucket.storage.downloadObject(
66+
bucket.name,
67+
imageFileName,
68+
);
69+
} on NotFoundException {
70+
throw InvalidArgumentError(
71+
'Image ${imageFileName} does not exist in bucket ${bucketName}.',
72+
);
73+
}
74+
75+
// Decode image
76+
final originalImage = decodeImage(Uint8List.fromList(originalBytes));
77+
if (originalImage == null) {
78+
throw InvalidArgumentError(
79+
'Failed to decode image. Are you sure it was an image file?',
80+
);
81+
}
82+
83+
// Resize if needed
84+
var encodedBytes;
85+
if (originalImage.width >= width) {
86+
final resizedImage = copyResize(
87+
originalImage,
88+
width: width,
89+
maintainAspect: true,
90+
);
91+
encodedBytes = encodeNamedImage(imageFileName, resizedImage);
92+
if (encodedBytes == null) {
93+
throw InternalError('Failed to encode resized image.');
94+
}
95+
} else {
96+
logger.info(
97+
'Image is already smaller than the requested width. No need to resize.',
98+
);
99+
encodedBytes = originalBytes;
100+
}
101+
102+
// Upload resized image to cache directory
103+
await bucket.storage.uploadObject(
104+
bucket.name,
105+
cachedFileName,
106+
encodedBytes,
107+
// Tell clients to cache the resized image to reduce repeat requests
108+
metadata: ObjectMetadata(cacheControl: 'public, max-age=86400'),
109+
);
110+
111+
// Return download URL
112+
final downloadUrl = await firebase.adminApp.storage().getDownloadURL(
113+
bucket,
114+
cachedFileName,
115+
);
116+
return Response.movedPermanently(downloadUrl);
117+
});
118+
});
119+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"functions": {
3+
"source": ".",
4+
"codebase": "dart-quickstarts-resize-image"
5+
},
6+
"emulators": {
7+
"functions": {
8+
"port": 5001
9+
},
10+
"storage": {
11+
"port": 9199
12+
},
13+
"ui": {
14+
"enabled": true
15+
},
16+
"singleProjectMode": true
17+
}
18+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: resize_image
2+
description: Image resizer example for Firebase Functions for Dart
3+
publish_to: none
4+
5+
environment:
6+
sdk: ^3.11.0
7+
8+
dependencies:
9+
image: ^4.8.0
10+
path: ^1.9.1
11+
firebase_functions:
12+
git:
13+
url: https://github.com/firebase/firebase-functions-dart
14+
ref: main
15+
intl: ^0.20.2
16+
17+
dev_dependencies:
18+
build_runner: ^2.10.5
19+
lints: ^6.0.0
20+
21+
dependency_overrides:
22+
firebase_admin_sdk:
23+
git:
24+
url: https://github.com/firebase/firebase-admin-dart
25+
path: packages/firebase_admin_sdk
26+
ref: main
27+
google_cloud_firestore:
28+
git:
29+
url: https://github.com/firebase/firebase-admin-dart
30+
path: packages/google_cloud_firestore
31+
ref: main

0 commit comments

Comments
 (0)