npm-storage-package
Version:
Universal cloud storage package for AWS S3, Google Cloud Storage, and Azure Blob Storage with Express.js integration
120 lines • 4.92 kB
JavaScript
import { storageAction } from "./storageAction.js";
// Custom multer storage engine for cloud storage
export class CloudStorageEngine {
provider;
config;
generateFileName;
fileFilter;
destination;
fieldConfigs;
fileSizeLimit;
constructor(options) {
this.provider = options.provider;
this.config = options.config;
this.generateFileName = options.generateFileName;
this.fileFilter = options.fileFilter;
this.destination = options.destination;
this.fieldConfigs = options.fieldConfigs;
this.fileSizeLimit = options.fileSizeLimit;
}
_handleFile(req, file, callback) {
// Get field-specific configuration
const fieldConfig = this.fieldConfigs?.[file.fieldname];
// Apply global file filter first
if (this.fileFilter && !this.fileFilter(req, file)) {
return callback(new Error("File type not allowed"));
}
// Apply field-specific extension validation
if (fieldConfig?.allowedExtensions) {
const fileExtension = file.originalname.toLowerCase().split(".").pop();
if (!fileExtension || !fieldConfig.allowedExtensions.includes(`.${fileExtension}`)) {
return callback(new Error(`File extension not allowed for field '${file.fieldname}'. Allowed: ${fieldConfig.allowedExtensions.join(", ")}`));
}
}
// Generate filename using field-specific or global generator
let fileName;
if (fieldConfig?.generateFileName) {
fileName = fieldConfig.generateFileName(req, file);
}
else if (this.generateFileName) {
fileName = this.generateFileName(req, file);
}
else {
// Use field-specific destination or global destination
const dest = fieldConfig?.destination ||
(typeof this.destination === "function" ? this.destination(req, file) : this.destination) ||
"";
const cleanDest = dest.replace(/[\\\/]+$/, ""); // Remove trailing slashes
fileName = cleanDest
? `${cleanDest}/${Date.now()}-${file.originalname}`
: `${Date.now()}-${file.originalname}`;
}
// Collect file data
const chunks = [];
let totalSize = 0;
file.stream.on("data", (chunk) => {
chunks.push(chunk);
totalSize += chunk.length;
// Check file size limit (global or field-specific)
const sizeLimit = this.fileSizeLimit;
if (sizeLimit && totalSize > sizeLimit) {
return callback(new Error(`File size exceeds limit of ${Math.round(sizeLimit / (1024 * 1024))}MB`));
}
});
file.stream.on("error", (error) => {
callback(error);
});
file.stream.on("end", async () => {
try {
// Combine all chunks into a single buffer
const buffer = Buffer.concat(chunks, totalSize);
// Upload to cloud storage
const uploadedUrl = await storageAction(this.provider, "upload", this.config, {
fileName,
buffer, // Pass buffer instead of file path
mimeType: file.mimetype,
});
// Return file info that multer expects
const fileInfo = {
fieldname: file.fieldname,
originalname: file.originalname,
encoding: file.encoding,
mimetype: file.mimetype,
size: totalSize,
destination: fieldConfig?.destination || this.destination || "",
filename: fileName,
path: uploadedUrl, // Use cloud URL as path
buffer,
cloudUrl: uploadedUrl,
cloudKey: fileName,
};
callback(null, fileInfo);
}
catch (error) {
callback(error);
}
});
}
_removeFile(req, file, callback) {
// Implement file removal from cloud storage
if (file.cloudKey) {
storageAction(this.provider, "delete", this.config, { key: file.cloudKey })
.then(() => callback(null))
.catch((error) => callback(error));
}
else {
callback(null);
}
}
}
// Factory functions to create storage engines
export function createS3Storage(options) {
return new CloudStorageEngine({ ...options, provider: "s3" });
}
export function createGCSStorage(options) {
return new CloudStorageEngine({ ...options, provider: "gcs" });
}
export function createAzureStorage(options) {
return new CloudStorageEngine({ ...options, provider: "azure" });
}
//# sourceMappingURL=multer-storage.js.map