UNPKG

multer-utility

Version:

A comprehensive TypeScript wrapper for Multer with enhanced features including memory/disk storage, flexible configuration, and file management utilities

305 lines 10.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MulterError = exports.createAudioUploader = exports.createImageUploader = exports.createMemoryUploader = exports.createDiskUploader = exports.UploadService = void 0; const multer_1 = __importStar(require("multer")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); class UploadService { constructor(config = {}) { /** * File filter for validation */ this.fileFilter = (req, file, cb) => { const validation = this.validateFile(file); if (validation.isValid) { cb(null, true); } else { cb(new multer_1.MulterError("LIMIT_UNEXPECTED_FILE", validation.error)); } }; this.config = { ...UploadService.DEFAULT_CONFIG, ...config }; if (this.config.storage === 'disk' && this.config.createDirIfNotExists) { this.ensureUploadDirExists(); } this.uploader = (0, multer_1.default)({ storage: this.getStorageEngine(), limits: { fileSize: this.config.maxFileSize }, fileFilter: this.fileFilter, }); } /** * Ensure upload directory exists */ ensureUploadDirExists() { if (!fs_1.default.existsSync(this.config.uploadDir)) { fs_1.default.mkdirSync(this.config.uploadDir, { recursive: true }); } } /** * Get storage engine based on configuration */ getStorageEngine() { if (this.config.storage === 'memory') { return multer_1.default.memoryStorage(); } return multer_1.default.diskStorage({ destination: (_, __, cb) => cb(null, this.config.uploadDir), filename: (_, file, cb) => { const filename = this.generateFilename(file); cb(null, filename); }, }); } /** * Generate filename based on configuration */ generateFilename(file) { // Use custom naming function if provided if (this.config.customNaming && this.config.customNaming(file)) { return this.config.customNaming(file); } let filename = file.originalname; // Preserve original name or generate new one if (!this.config.preserveOriginalName) { const ext = path_1.default.extname(file.originalname); const baseName = path_1.default.basename(file.originalname, ext); // Sanitize filename if (this.config.sanitizeFilenames) { filename = baseName.replace(/[^a-zA-Z0-9.-]/g, "_") + ext; } // Add timestamp if (this.config.useTimestamp) { const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); filename = `${timestamp}-${filename}`; } } return filename; } /** * Validate file against configured rules */ validateFile(file) { const ext = path_1.default.extname(file.originalname).toLowerCase(); // Check MIME type if (!this.config.allowedMimeTypes.includes(file.mimetype)) { return { isValid: false, error: `Invalid MIME type: ${file.mimetype}. Allowed: ${this.config.allowedMimeTypes.join(', ')}` }; } // Check file extension if (!this.config.allowedExtensions.includes(ext)) { return { isValid: false, error: `Invalid file extension: ${ext}. Allowed: ${this.config.allowedExtensions.join(', ')}` }; } return { isValid: true }; } /** * Upload single file */ single(fieldName) { return this.uploader.single(fieldName); } /** * Upload multiple files (array) */ array(fieldName, maxCount) { return this.uploader.array(fieldName, maxCount); } /** * Upload multiple files (different fields) */ fields(fields) { return this.uploader.fields(fields); } /** * Handle any files */ any() { return this.uploader.any(); } /** * No files expected */ none() { return this.uploader.none(); } /** * Get upload configuration */ getConfig() { return { ...this.config }; } /** * Get file info for uploaded file */ getFileInfo(file) { return { originalName: file.originalname, filename: file.filename || 'memory-storage', mimetype: file.mimetype, size: file.size, encoding: file.encoding, fieldname: file.fieldname, destination: file.destination, path: file.path, buffer: file.buffer, // Available in memory storage }; } /** * Delete uploaded file (disk storage only) */ async deleteFile(filename) { if (this.config.storage === 'memory') { throw new Error('Cannot delete file from memory storage'); } try { const filePath = path_1.default.join(this.config.uploadDir, filename); await fs_1.default.promises.unlink(filePath); return true; } catch (error) { return false; } } /** * Check if file exists (disk storage only) */ fileExists(filename) { if (this.config.storage === 'memory') { return false; } const filePath = path_1.default.join(this.config.uploadDir, filename); return fs_1.default.existsSync(filePath); } /** * Get file path (disk storage only) */ getFilePath(filename) { if (this.config.storage === 'memory') { throw new Error('Memory storage does not have file paths'); } return path_1.default.join(this.config.uploadDir, filename); } /** * Get upload directory stats */ async getUploadStats() { if (this.config.storage === 'memory') { return { message: 'Memory storage - no persistent files' }; } try { const files = await fs_1.default.promises.readdir(this.config.uploadDir); const stats = await Promise.all(files.map(async (file) => { const filePath = path_1.default.join(this.config.uploadDir, file); const stat = await fs_1.default.promises.stat(filePath); return { name: file, size: stat.size, created: stat.birthtime, modified: stat.mtime, }; })); return { totalFiles: files.length, totalSize: stats.reduce((acc, file) => acc + file.size, 0), files: stats, }; } catch (error) { throw new Error(`Failed to get upload stats: ${error}`); } } } exports.UploadService = UploadService; UploadService.DEFAULT_CONFIG = { storage: 'disk', uploadDir: path_1.default.join(process.cwd(), "uploads"), allowedMimeTypes: ["image/jpeg", "image/png", "image/gif", "audio/mpeg", "application/pdf"], allowedExtensions: [".jpg", ".jpeg", ".png", ".gif", ".mp3", ".pdf"], maxFileSize: 1024 * 1024 * 15, useTimestamp: true, sanitizeFilenames: true, customNaming: () => '', createDirIfNotExists: true, preserveOriginalName: false }; // Convenience functions for common use cases const createDiskUploader = (uploadDir, config) => { return new UploadService({ storage: 'disk', uploadDir, ...config }); }; exports.createDiskUploader = createDiskUploader; const createMemoryUploader = (config) => { return new UploadService({ storage: 'memory', ...config }); }; exports.createMemoryUploader = createMemoryUploader; const createImageUploader = (uploadDir) => { return new UploadService({ storage: 'disk', uploadDir: uploadDir || path_1.default.join(process.cwd(), 'uploads', 'images'), allowedMimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], allowedExtensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp'], maxFileSize: 1024 * 1024 * 5, // 5MB }); }; exports.createImageUploader = createImageUploader; const createAudioUploader = (uploadDir) => { return new UploadService({ storage: 'disk', uploadDir: uploadDir || path_1.default.join(process.cwd(), 'uploads', 'audio'), allowedMimeTypes: ['audio/mpeg', 'audio/wav', 'audio/ogg'], allowedExtensions: ['.mp3', '.wav', '.ogg'], maxFileSize: 1024 * 1024 * 50, // 50MB }); }; exports.createAudioUploader = createAudioUploader; // Export types var multer_2 = require("multer"); Object.defineProperty(exports, "MulterError", { enumerable: true, get: function () { return multer_2.MulterError; } }); //# sourceMappingURL=index.js.map