Last active
March 1, 2024 07:56
-
-
Save IsaiahByDayah/3c3e07532a966e3188e457f9fd957ea8 to your computer and use it in GitHub Desktop.
Example express endpoint that resizes an image from B2 Cloud Storage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as admin from "firebase-admin" | |
import express from "express" | |
import sharp, { ResizeOptions, OutputOptions } from "sharp" | |
import fetch from "cross-fetch" | |
import FileType from "file-type" | |
import { parseQueryNumber, parseQueryString } from "../some/extra/util/functions" | |
import { Photo } from "../some/custom/types" | |
const router = express.Router() | |
const DEFAULT_QUALITY = 80 | |
const DEFAULT_FORMAT = "jpg" | |
//REF: https://maxbarry.medium.com/dynamic-on-demand-image-resizing-using-firebase-hosting-and-google-cloud-functions-to-make-a-cheap-d64e8f5805d1 | |
router.get("/:path", async (req, res) => { | |
// From the URL we want to get our passed parameters | |
const { query, params } = req | |
// The image path | |
const { path } = params | |
console.log(`Trying to fetch image at path ${path}`) | |
// Parse these params to integers | |
let width = parseQueryNumber("w", query) ?? parseQueryNumber("width", query) | |
let height = parseQueryNumber("h", query) ?? parseQueryNumber("height", query) | |
const quality = | |
parseQueryNumber("q", query) ?? | |
parseQueryNumber("quality", query) ?? | |
DEFAULT_QUALITY | |
let format = | |
( | |
parseQueryString("f", query) ?? parseQueryString("format", query) | |
)?.toLowerCase() ?? DEFAULT_FORMAT | |
// If you don't have a filepath then return a 404 | |
if (!path?.length) { | |
console.log("No path supplied") | |
res.sendStatus(400) | |
return | |
} | |
const imageUrl = `{BASE B2 Url}/${path}` | |
console.log("Going to fetch image from: ", imageUrl) | |
// We're going to use Sharp.js to resize our image. | |
// Use the URL parameters to build options for Sharp | |
let resizeOpts: ResizeOptions = { width, height } | |
const formatOpts: OutputOptions = { | |
quality: Math.max(1, Math.min(100, quality)), | |
} | |
// We're going to use streams to do the following: | |
// read from our source image > pipe to Sharp > pipe to the HTTP response | |
// Read the remote file into that pipeline | |
let imageFetch: Response | |
try { | |
imageFetch = await fetch(imageUrl) | |
} catch (e) { | |
throw new Error("error with fetch call") | |
} | |
let imageArrayBuffer: ArrayBuffer | |
try { | |
imageArrayBuffer = await imageFetch.arrayBuffer() | |
} catch (e) { | |
throw new Error("error with convert to ArrayBuffer") | |
} | |
const imageBuffer = Buffer.from(imageArrayBuffer) | |
// Let's create a Sharp pipeline | |
let pipeline = sharp(imageBuffer) | |
// Now run the Sharp pipeline and pipe the output to the response | |
pipeline = pipeline.resize(resizeOpts) | |
// Reduce quality | |
pipeline = pipeline.toFormat(format, formatOpts) | |
// Write our content type response header | |
const fileType = await FileType.fromBuffer(await pipeline.toBuffer()) | |
res.contentType(fileType.mime) | |
// If you wanted to store the resized image in B2 you could do it here... | |
// Return the resized image to the client | |
pipeline.pipe(res) | |
}) | |
export default router |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment