initDynamicResourceMiddleware(options)
The initDynamicResourceMiddleware is a powerful Express middleware designed to serve files from one or more specified directories dynamically. Unlike initStaticResourceMiddleware, it actively watches for file system changes (additions, deletions, modifications) in real-time, making it ideal for serving content that can change during runtime, such as user-uploaded files, themes, or plugins.
It builds an in-memory map of resources for fast lookups and handles caching, file filtering, and conflict resolution gracefully.
How It Works#
The middleware follows a clear lifecycle: initialization, scanning, watching, and serving. When a request comes in, it performs a quick lookup in its internal map. If a file is added or removed from a watched directory, the map is updated automatically.
Basic Usage#
Here's how to configure the middleware to serve images from a dynamic uploads directory.
Server Setup
import express from 'express';
import { initDynamicResourceMiddleware } from '@blocklet/uploader-server';
import path from 'path';
const app = express();
const dynamicResourceMiddleware = initDynamicResourceMiddleware({
resourcePaths: [
{
path: path.join(__dirname, 'uploads/images'),
whitelist: ['.jpg', '.jpeg', '.png', '.gif'],
},
],
onReady: (count) => {
console.log(`${count} dynamic resources are ready to be served.`);
},
onFileChange: (filePath, event) => {
console.log(`File ${filePath} was ${event}.`);
},
});
// Mount the middleware
app.use('/uploads/images', dynamicResourceMiddleware);
// On server shutdown, clean up watchersSee all 10 lines
Configuration Options#
The initDynamicResourceMiddleware function accepts a single options object with the following properties:
Option | Type | Description |
|---|---|---|
|
| Optional. If provided, the middleware will only activate if the current component's DID matches this value. |
|
| Required. An array of objects defining the directories to watch and serve. See details below. |
|
| Optional. Configuration for the file system watcher. |
|
| Optional. Configuration for HTTP caching headers. |
|
| Optional. A callback function that triggers when a file is changed, added, or deleted. The |
|
| Optional. A callback that runs after the initial scan is complete and when the resource map changes, providing the total count of available resources. |
|
| Optional. A function to set custom headers on the response before serving a file. |
|
| Optional. Strategy to handle filename collisions when multiple directories contain a file with the same name. Defaults to |
DynamicResourcePath Object#
Each object in the resourcePaths array defines a source for dynamic assets.
Property | Type | Description |
|---|---|---|
|
| Required. The absolute path to the directory. It supports glob patterns (e.g., |
|
| Optional. An array of file extensions (e.g., |
|
| Optional. An array of file extensions to exclude. |
watchOptions Object#
Property | Type | Description |
|---|---|---|
|
| An array of string patterns or regular expressions to ignore during watching. |
|
| If |
|
| Whether to use polling for watching files. Can be necessary for certain network file systems. |
|
| The depth of subdirectories to watch. If |
cacheOptions Object#
Property | Type | Description |
|---|---|---|
|
| Sets the |
|
| If |
|
| Whether to enable ETag generation. |
|
| Whether to enable the |
Advanced Usage#
Using Glob Patterns#
To serve assets from multiple plugin directories, you can use a glob pattern. The middleware will find all matching directories and watch them for changes.
Glob Pattern Example
const middleware = initDynamicResourceMiddleware({
resourcePaths: [
{
// Watch the 'assets' folder inside every directory under 'plugins'
path: path.join(__dirname, 'plugins', '*', 'assets'),
whitelist: ['.css', '.js', '.png'],
},
],
});Conflict Resolution#
If two watched directories contain a file named logo.png, the conflictResolution strategy determines which one is served:
'first-match'(default): The first one found during the initial scan is used. Subsequent finds are ignored.'last-match': The last one found will overwrite any previous entry. This is useful if you have an override mechanism.'error': Logs an error to the console indicating a conflict, and typically the first-match behavior is used.
Return Value#
The initDynamicResourceMiddleware function returns an Express middleware function. This returned function also has a cleanup method attached to it.
cleanup()#
This method should be called during a graceful server shutdown. It stops all file system watchers and clears the internal resource maps to prevent memory leaks and release file handles.
Cleanup Example
const server = app.listen(3000);
const dynamicMiddleware = initDynamicResourceMiddleware(/* ...options */);
// ...
function gracefulShutdown() {
console.log('Shutting down server...');
if (dynamicMiddleware.cleanup) {
dynamicMiddleware.cleanup();
}
server.close(() => {
console.log('Server closed.');
process.exit(0);
});
}
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);