The initLocalStorageServer function is the core middleware for handling direct file uploads from a user's device to your blocklet's local storage. It leverages the robust Tus resumable upload protocol, ensuring that uploads are reliable and can be resumed after network interruptions.
This middleware is responsible for receiving file chunks, assembling them into a complete file on the server, and triggering callbacks for you to process the file metadata after the upload is complete.
How It Works
The following diagram illustrates the typical data flow when a user uploads a file using the Uploader component connected to a backend with initLocalStorageServer.
Basic Usage
To get started, initialize the middleware in your Express application and mount it on a specific route. The most critical option is onUploadFinish, which is where you'll define what happens after a file is successfully saved.
Basic Backend Setup
import express from 'express';
import { initLocalStorageServer } from '@blocklet/uploader-server';
import Upload from '../models/upload'; // Your database model
const router = express.Router();
// Initialize the uploader server middleware
const localStorageServer = initLocalStorageServer({
// The directory where uploaded files will be stored
path: process.env.UPLOAD_DIR,
express,
// This callback executes after a file is successfully uploaded
onUploadFinish: async (req, res, uploadMetadata) => {
const {
id: filename, // The unique, randomized filename on disk
size,
metadata: { filename: originalname, filetype: mimetype },
} = uploadMetadata;
// Construct the public URL for the uploaded file
const fileUrl = new URL(process.env.APP_URL);
fileUrl.pathname = `/uploads/${filename}`;
// Save file information to your database
const doc = await Upload.insert({
mimetype,
originalname,
filename,
size,
url: fileUrl.href,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
createdBy: req.user.did,
});
// Return the database document as a JSON response
// This data will be passed to the frontend's onUploadFinish callback
return doc;
},
});
// Mount the middleware on the '/uploads' route
// Ensure any necessary authentication/authorization middleware runs before it
router.use('/uploads', yourAuthMiddleware, localStorageServer.handle);
export default router;Configuration Options
The initLocalStorageServer function accepts an options object with the following properties:
| Option | Type | Required | Description |
|---|---|---|---|
path | string | Yes | The absolute path to the directory where uploaded files will be stored. |
express | Function | Yes | The Express application instance. |
onUploadFinish | Function | No | An async callback function that runs after a file upload is complete. It receives (req, res, uploadMetadata). The return value is sent as a JSON response to the frontend. |
onUploadCreate | Function | No | An async callback function that runs when a new upload is initiated but before any data is transferred. Useful for validation or authorization checks. It receives (req, res, uploadMetadata). |
expiredUploadTime | Number | No | The time in milliseconds after which incomplete uploads are considered expired and will be cleaned up by a background job. Default: 1000 * 60 * 60 * 24 * 3 (3 days). |
...restProps | object | No | Any other valid options for the underlying @tus/server package will be passed through. |
Callbacks in Detail
onUploadFinish(req, res, uploadMetadata)
This is the primary callback for processing completed uploads. It's the ideal place to save file metadata to your database, trigger webhooks, or perform other post-upload actions.
uploadMetadata Object
The uploadMetadata object passed to the callback contains detailed information about the uploaded file:
| Property | Type | Description |
|---|---|---|
id | string | The unique, randomly generated filename on the server's disk. |
size | number | The total size of the file in bytes. |
offset | number | The current number of bytes uploaded. Should equal size in this callback. |
metadata | object | An object containing metadata provided by the client. |
metadata.filename | string | The original filename from the user's computer. |
metadata.filetype | string | The MIME type of the file (e.g., image/jpeg). |
runtime | object | An object with runtime information about the file's location. |
runtime.absolutePath | string | The full path to the file on the server's filesystem. |
Return Value
The value you return from onUploadFinish will be serialized into JSON and sent back to the frontend Uploader component. This allows you to pass back the database record ID, the public URL, or any other relevant data.
Automatic Cleanup
The middleware automatically sets up a background cron job (auto-cleanup-expired-uploads) that runs hourly. This job safely removes any partial or expired uploads from the storage directory that have exceeded the expiredUploadTime, preventing your server from filling up with incomplete files.
Advanced Features
EXIF Data Removal
For privacy and security, the middleware automatically attempts to strip EXIF (Exchangeable image file format) metadata from uploaded images (.jpeg, .tiff, etc.) after the upload is complete.
Manual File Deletion
The returned server instance includes a delete method you can use to programmatically remove an uploaded file and its associated metadata file.
Manually Deleting a File
import { localStorageServer } from './setup'; // Assuming you exported the instance
async function deleteFile(filename) {
try {
await localStorageServer.delete(filename);
console.log(`Successfully deleted ${filename}`);
} catch (error) {
console.error(`Failed to delete ${filename}:`, error);
}
}Now that you know how to handle direct uploads, you might want to enable users to import files from external services. Proceed to the next section to learn about initCompanion.
Next: initCompanion(options)
Learn how to set up the Companion middleware to allow users to import files from remote sources like Unsplash and direct URLs.