@uploadx/core
Version:
Node.js resumable upload middleware
138 lines (137 loc) • 5.24 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DiskStorage = exports.DiskFile = void 0;
const path_1 = require("path");
const utils_1 = require("../utils");
const file_1 = require("./file");
const local_meta_storage_1 = require("./local-meta-storage");
const storage_1 = require("./storage");
class DiskFile extends file_1.File {
}
exports.DiskFile = DiskFile;
/**
* Local Disk Storage
*/
class DiskStorage extends storage_1.BaseStorage {
constructor(config = {}) {
super(config);
this.config = config;
this.checksumTypes = ['md5', 'sha1', 'sha256'];
this.directory = config.directory || this.path.replace(/^\//, '');
if (config.metaStorage) {
this.meta = config.metaStorage;
}
else {
const metaConfig = { ...config, ...config.metaStorageConfig };
this.meta = new local_meta_storage_1.LocalMetaStorage(metaConfig);
}
this.isReady = false;
this.accessCheck()
.then(() => (this.isReady = true))
.catch(err => {
this.logger.error('Storage access check failed: %O', err);
});
}
normalizeError(err) {
return super.normalizeError(err);
}
accessCheck() {
return (0, utils_1.accessCheck)(this.directory);
}
async create(req, fileInit) {
const file = new DiskFile(fileInit);
file.name = this.namingFunction(file, req);
file.size = Number.isNaN(file.size) ? this.maxUploadSize : file.size;
await this.validate(file);
const path = this.getFilePath(file.name);
file.bytesWritten = await (0, utils_1.ensureFile)(path).catch(err => (0, utils_1.fail)(utils_1.ERRORS.FILE_ERROR, err));
file.status = (0, file_1.getFileStatus)(file);
await this.saveMeta(file);
return file;
}
async write(part) {
const file = await this.getMeta(part.id);
await this.checkIfExpired(file);
if (file.status === 'completed')
return file;
if (part.size)
(0, file_1.updateSize)(file, part.size);
if (!(0, file_1.partMatch)(part, file))
return (0, utils_1.fail)(utils_1.ERRORS.FILE_CONFLICT);
const path = this.getFilePath(file.name);
await this.lock(path);
try {
if ((0, file_1.hasContent)(part)) {
if (this.isUnsupportedChecksum(part.checksumAlgorithm)) {
return (0, utils_1.fail)(utils_1.ERRORS.UNSUPPORTED_CHECKSUM_ALGORITHM);
}
const [bytesWritten, errorCode] = await this._write({ ...file, ...part });
if (errorCode) {
await (0, utils_1.truncateFile)(path, part.start);
return (0, utils_1.fail)(errorCode);
}
file.bytesWritten = bytesWritten;
file.status = (0, file_1.getFileStatus)(file);
await this.saveMeta(file);
}
else {
file.bytesWritten = await (0, utils_1.ensureFile)(path);
}
return file;
}
catch (err) {
return (0, utils_1.fail)(utils_1.ERRORS.FILE_ERROR, err);
}
finally {
await this.unlock(path);
}
}
async delete({ id }) {
try {
const file = await this.getMeta(id);
await (0, utils_1.removeFile)(this.getFilePath(file.name));
await this.deleteMeta(id);
return [{ ...file, status: 'deleted' }];
}
catch { }
return [{ id }];
}
/**
* Returns path for the uploaded file
*/
getFilePath(filename) {
return (0, path_1.join)(this.directory, filename);
}
_write(part) {
return new Promise((resolve, reject) => {
const dest = (0, utils_1.getWriteStream)(this.getFilePath(part.name), part.start);
const lengthChecker = (0, utils_1.streamLength)(part.contentLength || part.size - part.start);
const checksumChecker = (0, utils_1.streamChecksum)(part.checksum, part.checksumAlgorithm);
const keepPartial = !part.checksum;
const cleanupStreams = () => {
dest.close();
lengthChecker.destroy();
checksumChecker.destroy();
};
const failWithCode = (code) => {
cleanupStreams();
resolve([NaN, code]);
};
lengthChecker.on('error', () => failWithCode(utils_1.ERRORS.FILE_CONFLICT));
checksumChecker.on('error', () => failWithCode(utils_1.ERRORS.CHECKSUM_MISMATCH));
part.body.on('aborted', () => failWithCode(keepPartial ? undefined : utils_1.ERRORS.REQUEST_ABORTED));
part.body
.pipe(lengthChecker)
.pipe(checksumChecker)
.pipe(dest)
.on('error', (err) => {
cleanupStreams();
reject(err);
})
.on('finish', () => {
return resolve([part.start + dest.bytesWritten]);
});
});
}
}
exports.DiskStorage = DiskStorage;