oak-general-business/lib/features/extraFile.js

336 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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;