This package simplifies sending static content
basicSendFile
import { HeadersMap } from "@ublitzjs/core";
import { basicSendFile } from "@ublitzjs/static";
import { stat } from "node:fs/promises";
import { App } from "uWebSockets.js";
var server = App();
server.get(
"/video.mp4",
basicSendFile(
{
path: "public/video.mp4",
contentType: "video/mp4",
/**max file size (or max size you wnat to send) */
maxSize: (await stat("video.mp4")).size,
},
/**additional options*/
{
headers: HeadersMap.default,
/**log errors to console */
logs: true,
/**can't send more than this IF Range header is present AND has no end specified */
maxChunk: 1024 * 1024,
/**whether Range header is required */
requireRange: true,
},
/**advanced memory options */
{
/**goes to createReadStream */
highWaterMark: 64 * 1024,
/**see this in index.d.ts. This value is default */
minQ: 32,
}
) as any
);Code snippet above is same as this:
sendFile
import {
closure,
HeadersMap,
registerAbort,
type HttpRequest,
type HttpResponse,
} from "@ublitzjs/core";
import { getRanges, sendFile } from "@ublitzjs/static";
import { stat } from "node:fs/promises";
import { App } from "uWebSockets.js";
var server = App();
server.get(
"/video.mp4",
(await closure(async () => {
var maxSize = (await stat("public/video.mp4")).size;
var maxChunk = 1024 * 1024;
var logs = true;
var requireRange = true;
return async (res: HttpResponse, req: HttpRequest) => {
//#region get "Range" header
registerAbort(res);
var range = req.getHeader("range");
if (range) {
try {
var { 0: start, 1: end }: any = getRanges(
range,
maxSize - 1,
maxChunk
);
if (end - start + 1 > maxSize)
return res.cork(() =>
res.writeStatus("416").end("Range not satisfiable")
);
} catch (error) {
return res.writeStatus("400").end((error as any).message);
}
} else if (requireRange)
return res.writeStatus("400").end("Range header required");
//#endregion
var err = await sendFile(
{ res, path: "public/video.mp4", maxSize, contentType: "video/mp4" },
{ start, end, headers: HeadersMap.default }
);
if (err && logs) console.error("ERROR", err);
};
})) as any
);It recursively analyzes folder once, collect most important data (content-type, size) and returns you an object like this:
{ "folder1/fileName.html" : { CT:"text/html" , size: 100 } }
Usually is used with staticServe and staticServeMulti, and here are the examples
There are several rules of writing path to directory, so read them in Index.d.ts
await analyzeFolder(
/*directory*/ "public/nestedFolder",
/*options*/ {
/*clear unused mime/types (recommended if it is the last function call)*/
deleteMimesList: true,
/*regex of things you want to avoid analyzing*/
avoid: /(.git)|(.ts)|(.map)/,
/*whether to include 'atime', 'mtime', 'ctime', 'birthtime'. Can be used in @ublitzjs/sitemap package (coming soon)*/
includeDates: true,
}
);These functions are to serve static content (sure), but, contrary to dynamicServe, which uses file system each request, you are sending only analyzed files (via analyzeFolder).
They handle trailing slashed and automatically lookup index.html (as well as dynamicServe)
staticServe - serves 1 folder. Good if you needn't many paths.
staticServeMulti - serves many folders. Best used with wildcard route "/*"
staticServe
var server = App();
var staticMethods = staticServe(
{
dirPath: "public",
fullRoute: "/prefix/public", // url to look for
paths: await analyzeFolder("public", { deleteMimesList: false }), // files to serve
},
// additional options
{
logs: true, // log errors from get method
maxChunk: 1024 * 1024, // for range requests when no end byte is specified
headers: HeadersMap.default,
}
);
server
.get("/*", staticMethods.get as any)
.head("/*", staticMethods.head as any)
// if finds file - code 405 (wrong method), else - 404
.any("/*", staticMethods.any as any);staticServeMulti (for better example - check "tests" folder)
var staticMethods = staticServeMulti(
{
folders: [
{
// goes to file system
dir: "public",
// another built-in method, which returns regex
// it hcecks the url. If matched - checks paths
regex: urlStartsWith("/prefix/public"),
paths: await analyzeFolder("public", { deleteMimesList: false }),
},
],
// checks url when other folders failed.
fallback: {
dir: "meta",
paths: await analyzeFolder("meta", { deleteMimesList: true }),
},
},
// additional options as before... they are OPTIONAL
{}
);
server
.get("/*", staticMethods.get as any)
.head("/*", staticMethods.head as any)
.any("/*", staticMethods.any as any);This function differs from static ones in using file system each time, the request comes, instead of checking folder AND then looking up the url in an analyzed object.
Suits using for folders, contents of which you can't track or control (like uploads).
var dynamicMethods = dynamicServe(
/*route regex*/ urlStartsWith("/your-uploads"),
/*directory*/ "uploads",
{
avoid: /*doesn't send these files*/ /.special-upload/,
logs: true /*logging errors from sending file*/,
// setting your custom headers ( also is in static versions )
headers: new HeadersMap({ ...HeadersMap.baseObj })
.remove("Cross-Origin-Opener-Policy")
.prepare(),
maxChunk: 1024 * 1024,
// if Range header is required (if uploads are large videos, then it needs to be required)
requireRange: true,
}
);
server
.get("/*", dynamicMethods.get as any)
.head("/*", dynamicMethods.head as any)
.any("/*", dynamicMethods.any as any);