Skip to content

Instantly share code, notes, and snippets.

@julianrubisch
Last active January 2, 2020 16:43
Show Gist options
  • Save julianrubisch/795391a221e9f4a92db8a57cf908904a to your computer and use it in GitHub Desktop.
Save julianrubisch/795391a221e9f4a92db8a57cf908904a to your computer and use it in GitHub Desktop.
Express File Cache Middleware
// const cachedAssetPath = ...
try {
if (fs.existsSync(cachedAssetPath)) {
const response = await fetch(res.locals.fetchUrl, { method: "HEAD" });
res.locals.contentLength = response.headers.get("content-length");
res.locals.contentType = response.headers.get("content-type");
res.locals.buffer = fs.readFileSync(cachedAssetPath);
} else {
const blob = await (await fetch(res.locals.fetchUrl)).blob();
res.locals.buffer = Buffer.from(await blob.arrayBuffer(), "binary");
res.locals.contentType = blob.type;
res.localscontentLength = blob.size;
fs.writeFileSync(cachedAssetPath, res.locals.buffer);
}
next();
} catch (e) {
// in case fs.writeFileSync writes partial data and fails
if (fs.existsSync(cachedAssetPath)) {
fs.unlinkSync(cachedAssetPath);
}
res.status(500).send(e.message);
}
function encodeAssetCacheName(contentType, contentLength) {
return Buffer.from(`${contentType}:${contentLength}`).toString("base64");
}
function decodeAssetCacheName(encodedString) {
const decodedFileName = Buffer.from(encodedString, "base64").toString(
"ascii"
);
return decodedFileName.split(":");
}
// ...
if (fs.existsSync(assetCachePath)) {
const firstFile = fs.readdirSync(assetCachePath)[0];
const [contentType, contentLength] = middleWare.decodeAssetCacheName(
firstFile
);
res.locals.contentLength = contentLength;
res.locals.contentType = contentType;
res.locals.buffer = fs.readFileSync(`${assetCachePath}/${firstFile}`);
} else {
const blob = await (await fetch(res.locals.fetchUrl)).blob();
const fileName = middleWare.encodeAssetCacheName(blob.type, blob.size);
res.locals.buffer = Buffer.from(await blob.arrayBuffer(), "binary");
res.locals.contentType = blob.type;
res.locals.contentLength = blob.size;
fs.writeFileSync(`${assetCachePath}/${fileName}`, res.locals.buffer);
}
next();
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
exclude: [/\.map$/, /asset-manifest\.json$/],
importWorkboxFrom: "cdn",
runtimeCaching: [
{
urlPattern: new RegExp("https://images.ctfassets.net"),
handler: "networkFirst"
},
{
urlPattern: new RegExp("https://cdn.contentful.com"),
handler: "networkFirst"
},
{
urlPattern: new RegExp("http://video.ctfassets.net"),
handler: "networkFirst"
}
],
navigateFallback: publicUrl + "/index.html",
navigateFallbackBlacklist: [
// Exclude URLs starting with /_, as they're likely an API call
new RegExp("^/_")
// Exclude URLs containing a dot, as they're likely a resource in
// public/ and not a SPA route
// new RegExp("/[^/]+\\.[^/]+$")
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment