336 lines
11 KiB
JavaScript
336 lines
11 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.ExtraFile = void 0;
|
||
const Feature_1 = require("oak-frontend-base/es/types/Feature");
|
||
const upload_1 = require("oak-frontend-base/es/utils/upload");
|
||
const extraFile_1 = require("../utils/extraFile");
|
||
const assert_1 = require("oak-domain/lib/utils/assert");
|
||
const index_frontend_1 = require("../utils/cos/index.frontend");
|
||
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||
const Projection_1 = require("../types/Projection");
|
||
class ExtraFile extends Feature_1.Feature {
|
||
cache;
|
||
application;
|
||
files;
|
||
fileUpLoad;
|
||
uploadIdToTaskId = {};
|
||
constructor(cache, application) {
|
||
super();
|
||
this.cache = cache;
|
||
this.application = application;
|
||
this.files = {};
|
||
const up = new upload_1.Upload();
|
||
this.fileUpLoad = up;
|
||
}
|
||
registerCos(clazzes) {
|
||
clazzes.forEach((clazz) => (0, index_frontend_1.registerCos)(clazz));
|
||
}
|
||
addLocalFile(id, file) {
|
||
(0, assert_1.assert)(!this.files[id]);
|
||
this.files[id] = {
|
||
file,
|
||
state: 'local',
|
||
};
|
||
this.publish();
|
||
}
|
||
removeLocalFiles(ids) {
|
||
ids.forEach((id) => (0, lodash_1.unset)(this.files, id));
|
||
this.publish();
|
||
}
|
||
async upload(id) {
|
||
/**
|
||
* 这个函数假设了前台知道后台会产生modi的行为和数据结构,不是很好的设计
|
||
*/
|
||
let modiEntityId = '';
|
||
const getExtraFileData = () => {
|
||
const [extraFile] = this.cache.get('extraFile', {
|
||
data: {
|
||
...Projection_1.extraFileProjection,
|
||
enableChunkedUpload: 1,
|
||
chunkInfo: 1,
|
||
},
|
||
filter: {
|
||
id,
|
||
},
|
||
});
|
||
if (extraFile) {
|
||
return extraFile;
|
||
}
|
||
// 否则再去modi中查看
|
||
const [modi] = this.cache.get('modi', {
|
||
data: {
|
||
id: 1,
|
||
data: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
},
|
||
filter: {
|
||
targetEntity: 'extraFile',
|
||
action: 'create',
|
||
filter: {
|
||
id,
|
||
},
|
||
},
|
||
});
|
||
modiEntityId = modi.entityId;
|
||
return modi.data;
|
||
};
|
||
const extraFile = getExtraFileData();
|
||
(0, assert_1.assert)(extraFile && extraFile.uploadState === 'uploading');
|
||
const item = this.files[id];
|
||
(0, assert_1.assert)(item);
|
||
const { file, state } = item;
|
||
(0, assert_1.assert)(['local', 'failed'].includes(state));
|
||
item.state = 'uploading';
|
||
item.percentage = 0;
|
||
const cos = (0, index_frontend_1.getCos)(extraFile.origin);
|
||
try {
|
||
await this.doUpload({
|
||
extraFile: extraFile,
|
||
file: file,
|
||
cos,
|
||
});
|
||
}
|
||
catch (err) {
|
||
item.state = 'failed';
|
||
item.percentage = undefined;
|
||
this.publish();
|
||
throw err;
|
||
}
|
||
if (!cos.autoInform()) {
|
||
const informServer = async () => {
|
||
const operation = {
|
||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||
action: 'update',
|
||
data: {
|
||
uploadState: 'success',
|
||
},
|
||
filter: {
|
||
id,
|
||
},
|
||
};
|
||
await this.cache.exec('operate', {
|
||
entity: 'extraFile',
|
||
operation,
|
||
});
|
||
};
|
||
await informServer();
|
||
}
|
||
item.state = 'uploaded';
|
||
item.percentage = undefined;
|
||
this.publish();
|
||
}
|
||
// style 多媒体样式
|
||
getUrl(extraFile, style) {
|
||
if (!extraFile) {
|
||
return '';
|
||
}
|
||
if (extraFile?.isBridge && extraFile?.extra1) {
|
||
return this.cache.makeBridgeUrl(extraFile?.extra1);
|
||
}
|
||
const { id } = extraFile;
|
||
if (this.files[id]) {
|
||
const { file } = this.files[id];
|
||
if (typeof file === 'string') {
|
||
return file;
|
||
}
|
||
if (file instanceof File) {
|
||
return (0, extraFile_1.getFileURL)(file);
|
||
}
|
||
(0, assert_1.assert)(false, 'the incoming file is not supported');
|
||
}
|
||
const { origin, extra1 } = extraFile;
|
||
// if (origin === 'unknown') {
|
||
// return extra1 || '';
|
||
// }
|
||
if (!origin) {
|
||
return "unknown";
|
||
}
|
||
const cos = (0, index_frontend_1.getCos)(origin);
|
||
return cos.composeFileUrl({
|
||
application: this.application.getApplication(),
|
||
extraFile: extraFile,
|
||
style: style,
|
||
cache: this.cache,
|
||
});
|
||
}
|
||
getFileState(id) {
|
||
if (this.files[id]) {
|
||
return this.files[id];
|
||
}
|
||
}
|
||
getFileName(extraFile) {
|
||
const name = extraFile.filename +
|
||
(extraFile.extension ? `.${extraFile.extension}` : '');
|
||
return name;
|
||
}
|
||
formatBytes(size) {
|
||
return (0, extraFile_1.bytesToSize)(size);
|
||
}
|
||
async autoUpload(options) {
|
||
const { extraFile, file, style, getPercent, chunkOptions } = options;
|
||
const extraFileId = extraFile.id || (0, uuid_1.generateNewId)();
|
||
const applicationId = extraFile.applicationId || this.application.getApplicationId();
|
||
const fileSize = extraFile.size;
|
||
const enabledChunkUpload = chunkOptions && fileSize && chunkOptions.chunkSize && fileSize > chunkOptions.chunkSize;
|
||
await this.cache.operate('extraFile', {
|
||
action: 'create',
|
||
data: Object.assign(extraFile, {
|
||
id: extraFileId,
|
||
applicationId,
|
||
enableChunkedUpload: enabledChunkUpload ? true : false,
|
||
chunkInfo: enabledChunkUpload ? {
|
||
chunkSize: chunkOptions.chunkSize,
|
||
partCount: Math.ceil((extraFile.size || 0) / chunkOptions.chunkSize),
|
||
} : undefined,
|
||
}),
|
||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||
});
|
||
const [newExtraFile] = this.cache.get('extraFile', {
|
||
data: {
|
||
...Projection_1.extraFileProjection,
|
||
enableChunkedUpload: 1,
|
||
chunkInfo: 1,
|
||
},
|
||
filter: {
|
||
id: extraFileId,
|
||
},
|
||
});
|
||
const cos = (0, index_frontend_1.getCos)(newExtraFile.origin);
|
||
try {
|
||
await this.doUpload({
|
||
extraFile: newExtraFile,
|
||
file: file,
|
||
cos,
|
||
getPercent: getPercent,
|
||
parallelism: chunkOptions?.parallelism,
|
||
retryTimes: chunkOptions?.retryTimes,
|
||
retryDelay: chunkOptions?.retryDelay,
|
||
});
|
||
if (!cos.autoInform()) {
|
||
await this.cache.exec('operate', {
|
||
entity: 'extraFile',
|
||
operation: {
|
||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||
action: 'update',
|
||
data: {
|
||
uploadState: 'success',
|
||
},
|
||
filter: {
|
||
id: extraFileId,
|
||
},
|
||
},
|
||
});
|
||
}
|
||
this.publish();
|
||
return this.getUrl(newExtraFile, style);
|
||
}
|
||
catch (err) {
|
||
await this.cache.operate('extraFile', {
|
||
action: 'remove',
|
||
data: {},
|
||
filter: {
|
||
id: extraFileId,
|
||
},
|
||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||
});
|
||
this.publish();
|
||
throw err;
|
||
}
|
||
}
|
||
/**
|
||
* 中止上传
|
||
* @param extraFileId ExtraFile的ID
|
||
*/
|
||
abortUpload(extraFileId) {
|
||
// this.fileUpLoad?.abortUpload(extraFileId);
|
||
const taskIds = this.uploadIdToTaskId[extraFileId];
|
||
console.log('abortUpload', extraFileId, taskIds);
|
||
if (taskIds) {
|
||
taskIds.forEach((taskId) => {
|
||
this.fileUpLoad?.abortUpload(taskId);
|
||
});
|
||
}
|
||
}
|
||
// 私有
|
||
async doUpload(options) {
|
||
const { extraFile, file, cos, getPercent, parallelism, retryTimes, retryDelay } = options;
|
||
const taskIds = this.generateUploadId(extraFile);
|
||
const extraFileId = extraFile.id;
|
||
const enabledChunkUpload = extraFile.enableChunkedUpload;
|
||
this.uploadIdToTaskId[extraFile.id] = taskIds;
|
||
await cos.upload({
|
||
extraFile: extraFile,
|
||
uploadFn: this.fileUpLoad.uploadFile,
|
||
presignMultiPartUpload: async (from, to) => {
|
||
const res = await this.cache.exec('presignMultiPartUpload', {
|
||
extraFileId,
|
||
from,
|
||
to,
|
||
});
|
||
return res.result;
|
||
},
|
||
file: file,
|
||
uploadToAspect: this.uploadToAspect.bind(this),
|
||
getPercent: getPercent,
|
||
parallelism: parallelism,
|
||
retryTimes: retryTimes,
|
||
retryDelay: retryDelay,
|
||
onChunkSuccess: async (chunkInfo) => {
|
||
if (enabledChunkUpload) {
|
||
await this.cache.operate('extraFile', [{
|
||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||
action: 'update',
|
||
data: {
|
||
chunkInfo,
|
||
},
|
||
filter: {
|
||
id: extraFileId,
|
||
},
|
||
}]);
|
||
}
|
||
},
|
||
});
|
||
// 上传成功后删除uploadId映射
|
||
delete this.uploadIdToTaskId[extraFile.id];
|
||
if (enabledChunkUpload) {
|
||
await this.cache.exec('mergeChunkedUpload', {
|
||
extraFileId,
|
||
});
|
||
}
|
||
}
|
||
async uploadToAspect(file, name, // 文件的part name
|
||
aspectName, // 上传的aspect名
|
||
formData, // 上传的其它part参数
|
||
autoInform // 上传成功是否会自动通知server(若不会则需要前台显式通知)
|
||
) {
|
||
const formData2 = new FormData();
|
||
for (const key of Object.keys(formData)) {
|
||
formData2.append(key, formData[key]);
|
||
}
|
||
formData2.append(name || 'file', file);
|
||
const { result } = await this.cache.exec(aspectName, formData2);
|
||
return result;
|
||
}
|
||
/**
|
||
* 生成上传ID列表,请确保在cos实现时,该方法生成的ID与upload方法中调用的上传接口的任务ID一致
|
||
*/
|
||
generateUploadId(extraFile) {
|
||
const chunkInfo = extraFile.chunkInfo;
|
||
const uploadIds = [];
|
||
if (extraFile.enableChunkedUpload) {
|
||
for (let partNumber = 1; partNumber <= chunkInfo.partCount; partNumber++) {
|
||
if (!chunkInfo.parts[partNumber - 1]) {
|
||
uploadIds.push(`${extraFile.id}:${partNumber}`);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
uploadIds.push(`${extraFile.id}`);
|
||
}
|
||
return uploadIds;
|
||
}
|
||
}
|
||
exports.ExtraFile = ExtraFile;
|