UNPKG

multer-az-storage-blob

Version:

ES5/6 & Typescript friendly multer storage engine for Azure's blob storage using @azure/storage-blob.

320 lines 17 kB
"use strict"; // ********************************************************* // // This file is subject to the terms and conditions defined in // file 'LICENSE.txt', which is part of this source code package. // // ********************************************************* var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MulterAzureStorage = exports.MASError = void 0; // Node Modules var uuid_1 = require("uuid"); var path_1 = require("path"); var storage_blob_1 = require("@azure/storage-blob"); var identity_1 = require("@azure/identity"); // Custom error class var MASError = /** @class */ (function () { function MASError(message) { this.errorList = []; this.name = "Multer Azure Error"; this.message = message ? message : null; } return MASError; }()); exports.MASError = MASError; var MulterAzureStorage = /** @class */ (function () { function MulterAzureStorage(options) { var _a; this.DEFAULT_UPLOAD_CONTAINER = "default-container"; // Init error array var errorLength = 0; this._error = new MASError(); //check to allow these values in environment file options.accessKey = (options.accessKey || process.env.AZURE_STORAGE_ACCESS_KEY || null); options.accountName = (options.accountName || process.env.AZURE_STORAGE_ACCOUNT || null); options.sasToken = (options.sasToken || process.env.AZURE_STORAGE_SAS_TOKEN || null); options.connectionString = (options.connectionString || process.env.AZURE_STORAGE_CONNECTION_STRING || null); // Container name is required if (!options.containerName) { errorLength++; this._error.errorList.push(new Error("Missing required parameter: Azure container name.")); } //If explicitly connection string auth, or no auth specified but a connection string was provided else if (options.authenticationType === 'connection string' || (!options.authenticationType && options.connectionString)) { if (!options.connectionString) { //if explicitly connection string, make sure the string was provided errorLength++; this._error.errorList.push(new Error("Missing required parameter for connection string auth: Azure blob storage connection string.")); } else { this._blobServiceClient = storage_blob_1.BlobServiceClient.fromConnectionString(options.connectionString); } } //If this is not connection string auth then account name is required else if (!options.accountName) { errorLength++; this._error.errorList.push(new Error("Missing required parameter: Azure storage account name.")); } //If explicitly AD auth else if (options.authenticationType === 'azure ad') { var credential = new identity_1.DefaultAzureCredential(); this._blobServiceClient = new storage_blob_1.BlobServiceClient("https://".concat(options.accountName, ".blob.core.windows.net"), credential); } //If explicitly sas token auth, or no auth specified but a sas token was provided else if (options.authenticationType === 'sas token' || (!options.authenticationType && options.sasToken)) { if (!options.sasToken) { //if explicitly SAS token, make sure the token was provided errorLength++; this._error.errorList.push(new Error("Missing required parameter for SAS token auth: SAS token value.")); } else { this._blobServiceClient = new storage_blob_1.BlobServiceClient("https://".concat(options.accountName, ".blob.core.windows.net?").concat(options.sasToken)); } } //If explicitly name/key auth, or no auth specified but name/key was provided. else if (options.authenticationType === 'account name and key' || (!options.authenticationType && options.accessKey)) { if (!options.accessKey) { //If explicitly name/key, check for key. errorLength++; this._error.errorList.push(new Error("Missing required parameter for account name/key auth: Azure blob storage access key.")); } else { var sharedKeyCredential = new storage_blob_1.StorageSharedKeyCredential(options.accountName, options.accessKey); this._blobServiceClient = new storage_blob_1.BlobServiceClient("https://".concat(options.accountName, ".blob.core.windows.net"), sharedKeyCredential); } } else { errorLength++; this._error.errorList.push(new Error("No authentication information found. Check options or environment file.")); } // Vaidate errors before proceeding if (errorLength > 0) { var inflection = errorLength > 1 ? ["are", "s"] : ["is", ""]; this._error.message = "There ".concat(inflection[0], " ").concat(errorLength, " missing required parameter").concat(inflection[1], "."); throw this._error; } // Set proper container name switch (typeof options.containerName) { case "string": this._containerName = this._promisifyStaticValue(options.containerName); break; case "function": this._containerName = options.containerName; break; default: // Catch for if container name is provided but not a desired type this._containerName = this._promisifyStaticValue(this.DEFAULT_UPLOAD_CONTAINER); break; } // Check for metadata this._metadata = null; if (options.metadata) { switch (typeof options.metadata) { case "object": this._metadata = this._promisifyStaticObj(options.metadata); break; case "function": this._metadata = options.metadata; break; } } // Check for user defined properties this._contentSettings = null; if (options.contentSettings) { switch (typeof options.contentSettings) { case "object": this._contentSettings = this._promisifyStaticObj(options.contentSettings); break; case "function": this._contentSettings = options.contentSettings; break; } } // Set proper blob name this._blobName = (_a = options.blobName) !== null && _a !== void 0 ? _a : this._generateBlobName; } //fulfill Multer contract MulterAzureStorage.prototype._handleFile = function (req, file, callback) { var _a; return __awaiter(this, void 0, void 0, function () { var blobName, containerName, containerClient, blockBlobClient, contentSettings, uploadOptions, _b, blobProperties, intermediateFile, finalFile, err_1; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!(this._error.errorList.length > 0)) return [3 /*break*/, 1]; callback(this._error, null); return [3 /*break*/, 13]; case 1: _c.trys.push([1, 12, , 13]); return [4 /*yield*/, this._blobName(req, file)]; case 2: blobName = _c.sent(); return [4 /*yield*/, this._containerName(req, file)]; case 3: containerName = _c.sent(); containerClient = this._blobServiceClient.getContainerClient(containerName); // Create container if it doesnt exist return [4 /*yield*/, this._createContainerIfNotExists(containerName)]; case 4: // Create container if it doesnt exist _c.sent(); blockBlobClient = containerClient.getBlockBlobClient(blobName); contentSettings = void 0; if (!(this._contentSettings == null)) return [3 /*break*/, 5]; contentSettings = { contentType: file.mimetype, contentDisposition: 'inline' }; return [3 /*break*/, 7]; case 5: return [4 /*yield*/, this._contentSettings(req, file)]; case 6: contentSettings = (_c.sent()); _c.label = 7; case 7: uploadOptions = { blobHTTPHeaders: { blobContentType: contentSettings.contentType, blobContentDisposition: contentSettings.contentDisposition }, }; if (!this._metadata) return [3 /*break*/, 9]; _b = uploadOptions; return [4 /*yield*/, this._metadata(req, file)]; case 8: _b.metadata = (_c.sent()); _c.label = 9; case 9: return [4 /*yield*/, blockBlobClient.uploadStream(file.stream, file.stream.readableHighWaterMark, 5, uploadOptions)]; case 10: _c.sent(); return [4 /*yield*/, blockBlobClient.getProperties()]; case 11: blobProperties = _c.sent(); intermediateFile = { url: blockBlobClient.url, blobName: blockBlobClient.name, etag: blobProperties.etag, blobType: blobProperties.blobType, metadata: blobProperties.metadata, container: blockBlobClient.containerName, blobSize: (_a = blobProperties.contentLength) === null || _a === void 0 ? void 0 : _a.toString() }; finalFile = Object.assign({}, file, intermediateFile); callback(null, finalFile); return [3 /*break*/, 13]; case 12: err_1 = _c.sent(); callback(err_1, null); return [3 /*break*/, 13]; case 13: return [2 /*return*/]; } }); }); }; //fulfill Multer contract MulterAzureStorage.prototype._removeFile = function (req, file, callback) { return __awaiter(this, void 0, void 0, function () { var containerName, containerClient, exists, blobName, rFError_1; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!(this._error.errorList.length > 0)) return [3 /*break*/, 1]; callback(this._error); return [3 /*break*/, 9]; case 1: _a.trys.push([1, 8, , 9]); return [4 /*yield*/, this._containerName(req, file)]; case 2: containerName = _a.sent(); containerClient = this._blobServiceClient.getContainerClient(containerName); return [4 /*yield*/, containerClient.exists()]; case 3: exists = _a.sent(); if (!!exists) return [3 /*break*/, 4]; this._error.message = "Container ".concat(containerName, " does not exist on this account. Check options."); callback(this._error); return [3 /*break*/, 7]; case 4: return [4 /*yield*/, this._blobName(req, file)]; case 5: blobName = _a.sent(); return [4 /*yield*/, containerClient.deleteBlob(blobName)]; case 6: _a.sent(); callback(null); _a.label = 7; case 7: return [3 /*break*/, 9]; case 8: rFError_1 = _a.sent(); callback(rFError_1); return [3 /*break*/, 9]; case 9: return [2 /*return*/]; } }); }); }; /** Helpers */ MulterAzureStorage.prototype._createContainerIfNotExists = function (name) { var containerClient = this._blobServiceClient.getContainerClient(name); var options = {}; if (this._containerAccessLevel !== 'private') { //For private containers, do not pass the access header options.access = this._containerAccessLevel; } return containerClient.createIfNotExists(options); }; MulterAzureStorage.prototype._generateBlobName = function (_req, file) { return new Promise(function (resolve, _reject) { resolve("".concat(Date.now(), "-").concat((0, uuid_1.v4)()).concat((0, path_1.extname)(file.originalname))); }); }; MulterAzureStorage.prototype._promisifyStaticValue = function (value) { return function (_req, _file) { return new Promise(function (resolve, _reject) { resolve(value); }); }; }; MulterAzureStorage.prototype._promisifyStaticObj = function (value) { return function (_req, _file) { return new Promise(function (resolve, _reject) { resolve(value); }); }; }; return MulterAzureStorage; }()); exports.MulterAzureStorage = MulterAzureStorage; exports.default = MulterAzureStorage; //# sourceMappingURL=MulterAzureStorage.js.map