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).