A simple but powerful file upload and serving middleware for Node.js/Express.
Supports:
- File uploads with configurable limits
- MIME type validation
- Optional secure URLs with JWT tokens
- Automatic thumbnail generation for images
- Organized storage structure (by date)
- File metadata management
- Pagination, sorting, and statistics
- Hooks for upload/delete events
npm install uploady multer sharp mime-types jsonwebtoken uuidconst express = require("express");
const EasyFileServer = require("uploady");
const app = express();
const fileServer = new EasyFileServer({
storageDir: "./uploads", // where to save files
maxFileSize: 10 * 1024 * 1024, // 10 MB max
allowedMimeTypes: ["image/jpeg", "image/png", "application/pdf"],
organizeByDate: true, // put uploads in YYYY-MM-DD subfolders
autoThumbnail: true, // generate thumbnails for images
secureUrls: {
enabled: true, // use JWT-protected URLs
expires: 600, // expire in 600 seconds (10 min)
secret: "super-secret-key", // your JWT secret
},
});
// Middleware for file uploads
app.post(
"/upload",
fileServer.upload("files", 5), // <fieldName>, <maxCount>
(req, res) => {
res.json({
uploaded: req.uploadedFiles,
errors: req.uploadErrors || [],
});
}
);
// Serve uploaded files
app.use("/files", fileServer.serveFiles());
// Serve thumbnails
app.use("/thumbnails", fileServer.serveThumbnails());
// List files (with pagination)
app.get("/list", async (req, res) => {
const files = await fileServer.listFiles({
page: Number(req.query.page) || 1,
limit: 20,
sortBy: "uploadedAt", // uploadedAt | size | originalName
sortOrder: "desc", // asc | desc
});
res.json(files);
});
// Delete file
app.delete("/delete/:filename", async (req, res) => {
const success = await fileServer.deleteFile(req.params.filename);
res.json({ success });
});
// Stats
app.get("/stats", (req, res) => {
res.json(fileServer.getStats());
});
app.listen(3000, () => {
console.log("Server running at http://localhost:3000");
});| Option | Type | Default | Description |
|---|---|---|---|
storageDir |
string |
./uploads |
Where files are stored |
maxFileSize |
number |
2GB |
Max file size in bytes |
allowedMimeTypes |
string[] | null |
null |
List of allowed MIME types (null = allow all) |
organizeByDate |
boolean |
false |
Organize uploads in YYYY-MM-DD subfolders |
autoThumbnail |
boolean |
false |
Generate thumbnails for images |
secureUrls.enabled |
boolean |
false |
Enable JWT-protected URLs |
secureUrls.expires |
number |
600 (10 minutes) |
Expiration in seconds |
secureUrls.secret |
string |
random hex | Secret key for signing JWTs |
When enabled, files and thumbnails are served only with a valid JWT token.
const url = fileServer.generateSecureUrl("file.jpg");
console.log(url);
// -> /files/file.jpg?token=eyJhbGciOi...To generate thumbnail URLs:
const thumbUrl = fileServer.generateSecureThumbnailUrl("file.jpg");If autoThumbnail: true, image thumbnails are generated automatically (200x200 max, JPEG).
Thumbnails are accessible at /thumbnails/:filename_thumb.jpg.
Express middleware for handling file uploads.
fieldNameβ name of form field (default:"files")maxCountβ max number of files per request
Result:
req.uploadedFilesβ array of uploaded metadatareq.uploadErrorsβ array of errors
Express middleware for serving uploaded files.
Supports JWT validation when secureUrls.enabled is true.
Express middleware for serving thumbnails. Also supports JWT validation.
Returns a paginated list of file metadata.
Options:
page(default: 1)limit(default: 50)sortBy:"uploadedAt","size","originalName"sortOrder:"asc"|"desc"
Returns metadata for a single file (excluding internal paths).
Deletes file and its thumbnail (if exists). Returns true/false.
Returns server statistics:
{
"totalFiles": 12,
"totalSize": 1048576,
"totalSizeMB": 1,
"secureUrlsEnabled": true,
"storageDir": "/absolute/path/to/uploads"
}Register event hooks with .on(event, fn):
onUploadStart(req)onUploadComplete(req, uploadedFiles)onFileDelete(filename, meta)
Example:
fileServer.on("onUploadComplete", (req, uploadedFiles) => {
console.log("Files uploaded:", uploadedFiles);
});curl -F "files=@/path/to/image.jpg" http://localhost:3000/uploadResponse:
{
"uploaded": [
{
"originalName": "image.jpg",
"storedName": "1693847392-uuid-image.jpg",
"mime": "image/jpeg",
"size": 12345,
"uploadedAt": "2025-09-07T12:34:56.789Z",
"url": "/files/1693847392-uuid-image.jpg?token=..."
}
],
"errors": []
}- Use a database instead of in-memory
Mapin production for metadata persistence. - Ensure you set a strong
secureUrls.secretin production. - Thumbnails are JPEG only (
_thumb.jpg). - Organize by date if expecting many files (to avoid huge directories).