uploadio
Version:
Simple middleware for uploading files.
139 lines (128 loc) • 3.67 kB
JavaScript
const fs = require('fs');
const path = require('path');
const { Readable } = require('stream');
// Parameters for safe file name parsing.
const SAFE_FILE_NAME_REGEX = /[^\w-]/g;
const MAX_EXTENSION_LENGTH = 3;
// Parameters to generate unique temporary file names:
const TEMP_COUNTER_MAX = 65536;
const TEMP_PREFIX = 'tmp';
let tempCounter = 0;
/**
* Builds instance options from arguments objects(can't be arrow function).
* @returns {Object} - result options.
*/
const buildOptions = function () {
const result = {};
[...arguments].forEach((options) => {
if (!options || typeof options !== 'object') {
return true;
}
Object.keys(options).forEach((i) => (result[i] = options[i]));
});
return result;
};
/**
* Logs message to console if debug option set to true.
* @param {Object} options - options object.
* @param {string} msg - message to log.
* @returns {boolean} - false if debug is off.
*/
const debugLog = (options, msg) => {
const opts = options || {};
if (!opts.debug) {
return false;
}
console.log(`File upload: ${msg}`); // eslint-disable-line
return true;
};
/**
* Decodes uriEncoded file names.
* @param fileName {String} - file name to decode.
* @returns {String}
*/
const uriDecodeFileName = (opts, fileName) => {
return opts.uriDecodeFileNames ? decodeURIComponent(fileName) : fileName;
};
/**
* Generates unique temporary file name. e.g. tmp-5000-156788789789.
* @param {string} prefix - a prefix for generated unique file name.
* @returns {string}
*/
const getTempFilename = (prefix = TEMP_PREFIX) => {
tempCounter = tempCounter >= TEMP_COUNTER_MAX ? 1 : tempCounter + 1;
return `${prefix}-${tempCounter}-${Date.now()}`;
};
const parseFileName = (opts, fileName) => {
// Check fileName argument
if (!fileName || typeof fileName !== 'string') {
return getTempFilename();
}
// Cut off file name if it's lenght more then 255.
const fileLength = 255;
const parsedName =
fileName.length <= fileLength ? fileName : fileName.substr(0, fileLength);
// Decode file name if uriDecodeFileNames option set true.
return uriDecodeFileName(opts, parsedName);
};
/**
* Creates a folder for file specified in the path variable
* @param {Object} fileUploadOptions
* @param {string} filePath
* @returns {boolean}
*/
const checkAndMakeDir = (fileUploadOptions, filePath) => {
// Check upload options were set.
if (!fileUploadOptions) {
return false;
}
if (!fileUploadOptions.createParentPath) {
return false;
}
// Check whether folder for the file exists.
if (!filePath) {
return false;
}
const parentPath = path.dirname(filePath);
// Create folder if it doesn't exist.
if (!fs.existsSync(parentPath)) {
fs.mkdirSync(parentPath, {
recursive: true,
});
}
// Checks folder again and return a results.
return fs.existsSync(parentPath);
};
/**
* Builds request fields (using to build req.body and req.files)
* @param {Object} instance - request object.
* @param {string} field - field name.
* @param {any} value - field value.
* @returns {Object}
*/
const buildFields = (instance, field, value) => {
// Do nothing if value is not set.
if (value === null || value === undefined) {
return instance;
}
instance = instance || {};
// Non-array fields
if (!instance[field]) {
instance[field] = value;
return instance;
}
// Array fields
if (instance[field] instanceof Array) {
instance[field].push(value);
} else {
instance[field] = [instance[field], value];
}
return instance;
};
module.exports = {
buildOptions,
checkAndMakeDir,
debugLog,
parseFileName,
buildFields,
};