js-uploader
Version:
A JavaScript library for file upload
402 lines • 22 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AwsS3TaskHandler = void 0;
var tslib_1 = require("tslib");
var signature_v4_1 = require("@aws-sdk/signature-v4");
var sha256_js_1 = require("@aws-crypto/sha256-js");
var xml2js = require("xml2js");
var CommonsTaskHandler_1 = require("./CommonsTaskHandler");
var interface_1 = require("../../interface");
var rxjs_1 = require("rxjs");
var operators_1 = require("rxjs/operators");
var ajax_1 = require("rxjs/ajax");
var shared_1 = require("../../shared");
var SHA256_HEADER = 'x-amz-content-sha256';
var UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD';
var xml2jsParser = new xml2js.Parser();
var xml2jsBuilder = new xml2js.Builder();
var AwsS3TaskHandler = /** @class */ (function (_super) {
tslib_1.__extends(AwsS3TaskHandler, _super);
function AwsS3TaskHandler(task, uploaderOptions) {
var _this = _super.call(this, task, uploaderOptions) || this;
!AwsS3TaskHandler._overwrite && _this.processUploaderOptions();
return _this;
}
AwsS3TaskHandler.prototype.enable = function (task) {
var ossOptions = this.uploaderOptions.ossOptions;
return (ossOptions === null || ossOptions === void 0 ? void 0 : ossOptions.enable(task)) && ossOptions.provider === interface_1.OSSProvider.S3;
};
AwsS3TaskHandler.prototype.abort = function () {
this.abortTaskFiles();
_super.prototype.abort.call(this);
return this;
};
AwsS3TaskHandler.prototype.abortTaskFiles = function () {
var _this = this;
var sub = rxjs_1.of.apply(void 0, tslib_1.__spread(this.task.fileList)).pipe(operators_1.filter(function (file) {
var _a = _this.getFileExtraInfo(file), key = _a.key, uploadId = _a.uploadId;
return !!key && !!uploadId && file.status !== interface_1.StatusCode.Complete;
}), operators_1.mergeMap(function (file) {
var _a = _this.getFileExtraInfo(file), key = _a.key, uploadId = _a.uploadId;
return _this.abortMultipartUpload(key, uploadId);
}, 10))
.subscribe({
complete: function () {
sub.unsubscribe();
sub = null;
},
});
};
AwsS3TaskHandler.prototype.processUploaderOptions = function () {
var _this = this;
shared_1.Logger.info('🚀 ~ file: AwsS3TaskHandler.ts ~ line 42 ~ AwsS3TaskHandler ~ processUploaderOptions', this);
var uploaderOptions = this.uploaderOptions;
var ossOptions = uploaderOptions.ossOptions, beforeFileUploadComplete = uploaderOptions.beforeFileUploadComplete, beforeFileUploadStart = uploaderOptions.beforeFileUploadStart, beforeUploadResponseProcess = uploaderOptions.beforeUploadResponseProcess;
if (!(ossOptions === null || ossOptions === void 0 ? void 0 : ossOptions.enable) || (ossOptions === null || ossOptions === void 0 ? void 0 : ossOptions.provider) !== interface_1.OSSProvider.S3) {
throw new Error('ossOptions配置错误!');
}
var chunkSize = uploaderOptions.chunkSize, chunked = uploaderOptions.chunked, requestBodyProcessFn = uploaderOptions.requestBodyProcessFn, requestOptions = uploaderOptions.requestOptions;
var url = requestOptions.url, headers = requestOptions.headers, responseType = requestOptions.responseType, method = requestOptions.method;
uploaderOptions.chunkSize = chunked ? Math.max(chunkSize || 0, Math.pow(1024, 2) * 5) : chunkSize;
uploaderOptions.requestOptions.responseType = function (task, upfile, chunk) {
if (!_this.enable(task)) {
return typeof responseType === 'function' ? responseType(task, upfile, chunk) : responseType;
}
else {
return 'text';
}
};
uploaderOptions.requestOptions.method = function (task, upfile, chunk) {
if (!_this.enable(task)) {
return typeof method === 'function' ? method(task, upfile, chunk) : method;
}
else {
return 'PUT';
}
};
uploaderOptions.requestOptions.url = function (task, upfile, chunk) {
if (!_this.enable(task)) {
return _this.createObserverble(url, task, upfile, chunk).toPromise();
}
return _this.getRequestBaseURL()
.pipe(operators_1.map(function (baseURL) {
var _a = _this.getFileExtraInfo(upfile), key = _a.key, uploadId = _a.uploadId;
return baseURL + "/" + key + "?partNumber=" + (chunk.index + 1) + "&uploadId=" + uploadId;
}))
.toPromise();
};
uploaderOptions.requestOptions.headers = function (task, upfile, chunk) {
if (!_this.enable(task)) {
return _this.createObserverble(headers, task, upfile, chunk).toPromise();
}
return _this.getRequestBaseURL()
.pipe(operators_1.map(function (baseURL) {
var _a;
var _b = _this.getFileExtraInfo(upfile), key = _b.key, uploadId = _b.uploadId;
return {
url: baseURL + "/" + key,
method: uploaderOptions.requestOptions.method,
headers: (_a = {}, _a[SHA256_HEADER] = UNSIGNED_PAYLOAD, _a),
query: { partNumber: String(chunk.index + 1), uploadId: uploadId },
};
}), operators_1.switchMap(function (requestToSign) { return _this.signRequest(requestToSign); }), operators_1.map(function (_a) {
var headers = _a.headers;
return tslib_1.__assign({ 'Content-Type': 'application/octet-stream; charset=UTF-8' }, headers);
}))
.toPromise();
};
uploaderOptions.requestBodyProcessFn = function (task, upfile, chunk, params) {
if (_this.enable(task)) {
return params.file;
}
else {
return requestBodyProcessFn === null || requestBodyProcessFn === void 0 ? void 0 : requestBodyProcessFn(task, upfile, chunk, params);
}
};
var overwriteFns = this.getOverwriteFns();
if ((beforeFileUploadComplete === null || beforeFileUploadComplete === void 0 ? void 0 : beforeFileUploadComplete.name) !== overwriteFns.overwriteBeforeFileUploadComplete.name) {
uploaderOptions.beforeFileUploadComplete = overwriteFns.overwriteBeforeFileUploadComplete;
}
if ((beforeFileUploadStart === null || beforeFileUploadStart === void 0 ? void 0 : beforeFileUploadStart.name) !== overwriteFns.overwriteBeforeFileUploadStart.name) {
uploaderOptions.beforeFileUploadStart = overwriteFns.overwriteBeforeFileUploadStart;
}
if ((beforeUploadResponseProcess === null || beforeUploadResponseProcess === void 0 ? void 0 : beforeUploadResponseProcess.name) !== overwriteFns.overwriteBeforeUploadResponseProcess.name) {
uploaderOptions.beforeUploadResponseProcess = overwriteFns.overwriteBeforeUploadResponseProcess;
}
AwsS3TaskHandler._overwrite = true;
};
AwsS3TaskHandler.prototype.getOverwriteFns = function () {
var _this = this;
var uploaderOptions = this.uploaderOptions;
var beforeFileUploadComplete = uploaderOptions.beforeFileUploadComplete, beforeFileUploadStart = uploaderOptions.beforeFileUploadStart, beforeUploadResponseProcess = uploaderOptions.beforeUploadResponseProcess;
return {
overwriteBeforeFileUploadStart: function (task, upFile) {
var extraInfo = _this.getFileExtraInfo(upFile);
var beforeUpload = function () {
return (beforeFileUploadStart === null || beforeFileUploadStart === void 0 ? void 0 : beforeFileUploadStart(task, upFile)) || Promise.resolve();
};
if (!_this.enable(task)) {
return beforeUpload();
}
var getObjectKey = function () {
var _a, _b;
var objectKey = ((_b = (_a = uploaderOptions.ossOptions) === null || _a === void 0 ? void 0 : _a.keyGenerator) === null || _b === void 0 ? void 0 : _b.call(_a, upFile, task)) || Promise.resolve('');
return _this.toObserverble(objectKey).pipe(operators_1.map(function (key) {
return (extraInfo.key = key);
}));
};
var createMultipartUpload = function () {
if (extraInfo.uploadId) {
return rxjs_1.of(extraInfo);
}
return getObjectKey().pipe(operators_1.switchMap(function (key) { return _this.createMultipartUpload(key); }), operators_1.tap(function (_a) {
var uploadId = _a.uploadId, bucket = _a.bucket;
return Object.assign(extraInfo, { uploadId: uploadId, bucket: bucket });
}));
};
return rxjs_1.of(null).pipe(operators_1.concatMap(createMultipartUpload), operators_1.concatMap(beforeUpload)).toPromise();
},
overwriteBeforeFileUploadComplete: function (task, file) {
var beforeFileComplete = function () { return (beforeFileUploadComplete === null || beforeFileUploadComplete === void 0 ? void 0 : beforeFileUploadComplete(task, file)) || Promise.resolve(); };
if (!_this.enable(task)) {
return beforeFileComplete();
}
var completeMultipartUpload = function () {
if (file.response.etag) {
return rxjs_1.of(file.response);
}
return new rxjs_1.Observable(function (subscriber) {
var _a;
var _b = _this.getFileExtraInfo(file), key = _b.key, uploadId = _b.uploadId;
var parts = (_a = file.chunkList) === null || _a === void 0 ? void 0 : _a.map(function (ck) { return ({
ETag: ck.response.etag,
PartNumber: ck.index + 1,
}); });
_this.completeMultipartUpload(key, uploadId, parts)
.pipe(operators_1.tap(function (res) {
file.response = res;
}))
.subscribe(subscriber);
});
};
return rxjs_1.of(null).pipe(operators_1.concatMap(completeMultipartUpload), operators_1.concatMap(beforeFileComplete)).toPromise();
},
overwriteBeforeUploadResponseProcess: function (task, file, chunk, response) {
var _a;
if (!_this.enable(task)) {
return Promise.resolve();
}
var etag = ((_a = response.xhr.getResponseHeader('etag')) === null || _a === void 0 ? void 0 : _a.replace(/['"]/g, '')) || '';
response.response = { etag: etag };
return (beforeUploadResponseProcess === null || beforeUploadResponseProcess === void 0 ? void 0 : beforeUploadResponseProcess(task, file, chunk, response)) || Promise.resolve();
},
};
};
AwsS3TaskHandler.prototype.createMultipartUpload = function (key) {
var _this = this;
var job = function (baseURL) {
var requestToSign = {
url: baseURL + "/" + key + "?uploads",
method: 'POST',
query: { uploads: '' },
};
return rxjs_1.from(_this.signRequest(requestToSign)).pipe(operators_1.switchMap(function (_a) {
var method = _a.method, body = _a.body, headers = _a.headers;
return ajax_1.ajax({
url: requestToSign.url,
method: method,
body: body,
headers: headers,
responseType: 'text',
}).pipe(operators_1.switchMap(function (res) { return rxjs_1.from(xml2jsParser.parseStringPromise(res.response)); }), operators_1.map(function (res) {
var _a = res === null || res === void 0 ? void 0 : res.InitiateMultipartUploadResult, Bucket = _a.Bucket, Key = _a.Key, UploadId = _a.UploadId;
return { bucket: Bucket[0], key: Key[0], uploadId: UploadId[0] };
}));
}));
};
return this.getRequestBaseURL().pipe(operators_1.switchMap(job));
};
AwsS3TaskHandler.prototype.uploadPart = function (key, partNumber, uploadId, body) {
var _this = this;
var job = function (baseURL) {
var _a;
var requestToSign = {
url: baseURL + "/" + key,
method: 'PUT',
headers: (_a = {}, _a[SHA256_HEADER] = UNSIGNED_PAYLOAD, _a),
query: { partNumber: String(partNumber), uploadId: uploadId },
body: body,
};
return rxjs_1.from(_this.signRequest(requestToSign)).pipe(operators_1.switchMap(function (_a) {
var query = _a.query, method = _a.method, body = _a.body, headers = _a.headers;
var queryString = _this.stringifyQuery(query);
var url = queryString ? requestToSign.url + '?' + queryString : requestToSign.url;
return ajax_1.ajax({
url: url,
method: method,
body: body,
headers: tslib_1.__assign({ 'Content-Type': 'application/octet-stream; charset=UTF-8' }, headers),
responseType: 'text',
}).pipe(operators_1.map(function (res) {
var _a;
var etag = ((_a = res.xhr.getResponseHeader('etag')) === null || _a === void 0 ? void 0 : _a.replace(/['"]/g, '')) || '';
return { uploadId: uploadId, key: key, partNumber: partNumber, etag: etag };
}));
}));
};
return this.getRequestBaseURL().pipe(operators_1.switchMap(job));
};
AwsS3TaskHandler.prototype.completeMultipartUpload = function (key, uploadId, parts) {
var _this = this;
var job = function (baseURL) {
var _a;
var requestToSign = {
url: baseURL + "/" + key,
method: 'POST',
query: { uploadId: uploadId },
headers: (_a = {}, _a[SHA256_HEADER] = UNSIGNED_PAYLOAD, _a),
body: xml2jsBuilder.buildObject({
CompleteMultipartUpload: {
$: {
xmlns: 'http://s3.amazonaws.com/doc/2006-03-01/',
},
Part: parts.sort(function (a, b) { return a.PartNumber - b.PartNumber; }),
},
}),
};
return rxjs_1.from(_this.signRequest(requestToSign)).pipe(operators_1.switchMap(function (_a) {
var method = _a.method, body = _a.body, headers = _a.headers, query = _a.query;
var queryString = _this.stringifyQuery(query);
var url = queryString ? requestToSign.url + '?' + queryString : requestToSign.url;
return ajax_1.ajax({
url: url,
method: method,
body: body,
headers: tslib_1.__assign({ 'Content-Type': 'application/octet-stream; charset=UTF-8' }, headers),
responseType: 'text',
}).pipe(operators_1.switchMap(function (res) { return rxjs_1.from(xml2jsParser.parseStringPromise(res.response)); }), operators_1.map(function (res) {
var _a = res.CompleteMultipartUploadResult || {}, Bucket = _a.Bucket, ETag = _a.ETag, Key = _a.Key;
return {
uploadId: uploadId,
bucket: Bucket[0],
etag: ETag[0],
key: Key[0],
};
}));
}));
};
return this.getRequestBaseURL().pipe(operators_1.switchMap(job));
};
AwsS3TaskHandler.prototype.abortMultipartUpload = function (key, uploadId) {
var _this = this;
var job = function (baseURL) {
var _a;
var requestToSign = {
url: baseURL + "/" + key,
method: 'DELETE',
query: { uploadId: uploadId },
headers: (_a = {}, _a[SHA256_HEADER] = UNSIGNED_PAYLOAD, _a),
};
return rxjs_1.from(_this.signRequest(requestToSign)).pipe(operators_1.switchMap(function (_a) {
var method = _a.method, headers = _a.headers, query = _a.query;
var queryString = _this.stringifyQuery(query);
var url = queryString ? requestToSign.url + '?' + queryString : requestToSign.url;
return ajax_1.ajax({
url: url,
method: method,
headers: headers,
responseType: 'text',
}).pipe(operators_1.mapTo(true), operators_1.catchError(function () { return rxjs_1.of(false); }));
}));
};
return this.getRequestBaseURL().pipe(operators_1.switchMap(job));
};
AwsS3TaskHandler.prototype.signRequest = function (requestToSign, signatureV4) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var url, method, query, headers, body, _a, pathname, hostname, host, protocol, origin, _b, signed;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
shared_1.Logger.info('🚀 ~ file: AwsS3TaskHandler.ts ~ line 324 ~ AwsS3TaskHandler ~ signRequest ~ requestToSign', requestToSign);
url = requestToSign.url, method = requestToSign.method, query = requestToSign.query, headers = requestToSign.headers, body = requestToSign.body;
_a = new URL(url), pathname = _a.pathname, hostname = _a.hostname, host = _a.host, protocol = _a.protocol, origin = _a.origin;
headers = Object.assign(headers || {}, { host: host });
url = origin + pathname;
if (!(signatureV4 !== null && signatureV4 !== void 0)) return [3 /*break*/, 1];
_b = signatureV4;
return [3 /*break*/, 3];
case 1: return [4 /*yield*/, this.getRequestSigner().toPromise()];
case 2:
_b = (_c.sent());
_c.label = 3;
case 3:
signatureV4 = _b;
return [4 /*yield*/, signatureV4.sign({
method: method,
headers: headers,
query: query,
path: pathname,
hostname: hostname,
protocol: protocol,
body: body,
})];
case 4:
signed = _c.sent();
shared_1.Logger.info('🚀 ~ file: AwsS3TaskHandler.ts ~ line 79 ~ AwsS3TaskHandler ~ signRequest ~ signed', signed);
delete signed.headers.host;
return [2 /*return*/, signed];
}
});
});
};
AwsS3TaskHandler.prototype.stringifyQuery = function (params) {
if (params === void 0) { params = {}; }
return Object.keys(params)
.map(function (k) { return k + "=" + params[k]; })
.join('&');
};
AwsS3TaskHandler.prototype.getS3Config = function () {
var _this = this;
if (this.s3Config) {
return rxjs_1.of(this.s3Config);
}
var ossOptions = this.uploaderOptions.ossOptions;
return this.createObserverble(ossOptions.s3Config).pipe(operators_1.tap(function (s3config) {
_this.s3Config = s3config;
}));
};
AwsS3TaskHandler.prototype.getRequestBaseURL = function () {
var _this = this;
if (this.requestBaseURL && /^https?:\/\//.test(this.requestBaseURL)) {
return rxjs_1.of(this.requestBaseURL);
}
return this.getS3Config().pipe(operators_1.map(function (s3Config) {
var endpoint = s3Config.endpoint;
return (_this.requestBaseURL = (endpoint === null || endpoint === void 0 ? void 0 : endpoint.protocol) + "//" + (endpoint === null || endpoint === void 0 ? void 0 : endpoint.hostname));
}));
};
AwsS3TaskHandler.prototype.getRequestSigner = function () {
var _this = this;
if (this.requestSigner) {
return rxjs_1.of(this.requestSigner);
}
return this.getS3Config().pipe(operators_1.map(function (s3Config) {
var _a = s3Config, region = _a.region, credentials = _a.credentials;
return (_this.requestSigner = new signature_v4_1.SignatureV4({
service: 's3',
credentials: credentials,
region: region,
sha256: sha256_js_1.Sha256,
uriEscapePath: false,
}));
}));
};
AwsS3TaskHandler.prototype.getFileExtraInfo = function (file) {
file.extraInfo = file.extraInfo || {};
return file.extraInfo;
};
AwsS3TaskHandler._overwrite = false;
return AwsS3TaskHandler;
}(CommonsTaskHandler_1.CommonsTaskHandler));
exports.AwsS3TaskHandler = AwsS3TaskHandler;
//# sourceMappingURL=AwsS3TaskHandler.js.map