oak-general-business/es/utils/cos/aliyun.backend.js

231 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.

import { assert } from 'oak-domain/lib/utils/assert';
import ALiYun from './aliyun';
import { ALiYunSDK } from 'oak-external-sdk';
import { OakPreConditionUnsetException } from 'oak-domain/lib/types/Exception';
import { stsAssumeRole } from 'oak-external-sdk/lib/service/ali/sts';
import { OakException } from 'oak-domain/lib/types';
export default class ALiYunBackend extends ALiYun {
getConfigAndInstance(application) {
const { config, account } = this.getConfig(application);
const instance = ALiYunSDK.getInstance(account.accessKeyId, account.accessKeySecret);
return {
config,
instance,
account,
};
}
async composeFileUrlBackend(options) {
const { application, extraFile, context, style } = options;
const { config: aliyunCosConfig } = this.getConfig(application);
if (aliyunCosConfig) {
let bucket = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
if (bucket) {
const { domain, protocol } = bucket;
let protocol2 = protocol;
if (protocol instanceof Array) {
// protocol存在https: 说明域名有证书
const index = protocol.includes('https:')
? protocol.findIndex((ele) => ele === 'https:')
: 0;
protocol2 = protocol[index];
}
return `${protocol2}//${domain}/${this.formKey(extraFile)}${style ? style : ''}`;
}
}
return '';
}
async formUploadMeta(application, extraFile) {
const { bucket, enableChunkedUpload } = extraFile;
// 构造文件上传所需的key
const key = this.formKey(extraFile);
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
const { buckets } = aliyunCosConfig;
let bucket2 = bucket;
if (!bucket2) {
const { defaultBucket } = aliyunCosConfig;
bucket2 = defaultBucket;
}
assert(bucket2);
const b = buckets.find((ele) => ele.name === bucket2);
assert(b, `${bucket2}不是一个有效的桶配置`);
Object.assign(extraFile, {
bucket: bucket2,
uploadMeta: !enableChunkedUpload ? instance.getUploadInfo(bucket2, b.zone, key) : {},
});
}
async checkWhetherSuccess(application, extraFile) {
const key = this.formKey(extraFile);
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
assert(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}`);
try {
const result = await instance.isExistObject(extraFile.bucket, b.zone, key);
return result;
}
catch (err) {
throw err;
}
}
async removeFile(application, extraFile) {
const key = this.formKey(extraFile);
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
assert(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}`);
try {
await instance.removeFile(extraFile.bucket, b.zone, key);
}
catch (err) {
throw err;
}
}
async composeChunkUploadInfo(application, extraFile, context) {
const key = this.formKey(extraFile);
const { instance, config: aliyunCosConfig, account } = this.getConfigAndInstance(application);
let useSts = true;
let stsInfo = {};
if (!account.stsEndpoint || !account.roleArn || !account.roleSessionName) {
useSts = false;
console.warn("阿里云Cos配置中缺少sts相关配置无法使用sts方式上传分片将使用账号授权进行上传可能存在安全风险请检查确保不会暴露accessKey");
}
else {
try {
const res = await stsAssumeRole({
accessKeyId: account.accessKeyId,
accessKeySecret: account.accessKeySecret,
endpoint: account.stsEndpoint,
roleArn: account.roleArn,
roleSessionName: account.roleSessionName,
});
stsInfo = {
stsToken: res.Credentials.SecurityToken,
accessKeyId: res.Credentials.AccessKeyId,
accessKeySecret: res.Credentials.AccessKeySecret,
};
}
catch (err) {
console.error("Failed to assume role for STS:", err);
throw new OakPreConditionUnsetException("获取阿里云STS临时凭证失败请检查配置是否正确", 'extraFile');
}
}
// 大部分校验都在formUploadMeta中完成这里可以不多做判断了
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
const preInit = await instance.prepareMultipartUpload(extraFile.bucket, b.zone, key, extraFile.chunkInfo?.partCount, {
timeout: 30 * 1000, // 30 seconds
...(useSts ? stsInfo
: {}),
expires: 3 * 24 * 60 * 60, // 3 days
});
return {
uploadId: preInit.uploadId,
chunkSize: extraFile.chunkInfo?.chunkSize,
partCount: preInit.parts.length,
parts: preInit.parts.map((part) => ({
partNumber: part.partNumber,
uploadUrl: part.uploadUrl,
formData: {}, // 阿里云不需要额外的formData
})),
};
}
/**
* 完成分片上传后的合并操作
*/
async mergeChunkedUpload(application, extraFile, parts, context) {
const key = this.formKey(extraFile);
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
assert(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}`);
assert(extraFile.chunkInfo?.uploadId, 'extraFile缺少chunkInfo.uploadId无法完成分片上传的合并操作');
assert(parts.length > 0, 'parts不能为空无法完成分片上传的合并操作');
try {
await instance.completeMultipartUpload(extraFile.bucket, b.zone, key, extraFile.chunkInfo.uploadId, parts.map(part => ({
number: part.partNumber,
etag: part.etag,
})));
}
catch (err) {
throw new OakException('合并分片上传失败' + 'extraFile' + err);
}
}
async abortMultipartUpload(application, extraFile, context) {
const key = this.formKey(extraFile);
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
assert(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}`);
assert(extraFile.chunkInfo?.uploadId, 'extraFile缺少chunkInfo.uploadId无法中止分片上传操作');
await instance.abortMultipartUpload(extraFile.bucket, b.zone, key, extraFile.chunkInfo.uploadId);
}
async listMultipartUploads(application, extraFile, context) {
const key = this.formKey(extraFile);
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
assert(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}`);
assert(extraFile.chunkInfo?.uploadId, 'extraFile缺少chunkInfo.uploadId无法列出分片上传信息');
return await instance.listParts(extraFile.bucket, b.zone, key, extraFile.chunkInfo.uploadId);
}
async presignFile(methods, application, extraFile, context) {
const key = this.formKey(extraFile);
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
assert(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}`);
return await instance.presignObjectUrl(methods, extraFile.bucket, b.zone, key, {
expires: 24 * 60 * 60, // 1 day
});
}
/**
* 对一段文件的分片上传进行预签名
* @param extraFileId extraFile的id
* @param from 起始partNumber
* @param to 结束partNumber
*/
async presignMultiPartUpload(application, extraFile, from, to, context) {
const key = this.formKey(extraFile);
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
assert(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}`);
const res = await instance.presignMulti(extraFile.bucket, b.zone, key, extraFile.chunkInfo.uploadId, from, to, {
expires: 24 * 60 * 60, // 1 day
});
return res;
}
async prepareChunkedUpload(application, extraFile, context) {
const key = this.formKey(extraFile);
const { instance, config: aliyunCosConfig, account } = this.getConfigAndInstance(application);
let useSts = true;
let stsInfo = {};
if (!account.stsEndpoint || !account.roleArn || !account.roleSessionName) {
useSts = false;
console.warn("阿里云Cos配置中缺少sts相关配置无法使用sts方式上传分片将使用账号授权进行上传可能存在安全风险请检查确保不会暴露accessKey");
}
else {
try {
const res = await stsAssumeRole({
accessKeyId: account.accessKeyId,
accessKeySecret: account.accessKeySecret,
endpoint: account.stsEndpoint,
roleArn: account.roleArn,
roleSessionName: account.roleSessionName,
});
stsInfo = {
stsToken: res.Credentials.SecurityToken,
accessKeyId: res.Credentials.AccessKeyId,
accessKeySecret: res.Credentials.AccessKeySecret,
};
}
catch (err) {
console.error("Failed to assume role for STS:", err);
throw new OakPreConditionUnsetException("获取阿里云STS临时凭证失败请检查配置是否正确", 'extraFile');
}
}
// 大部分校验都在formUploadMeta中完成这里可以不多做判断了
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
const preInit = await instance.initiateMultipartUpload(extraFile.bucket, b.zone, key, {
timeout: 30 * 1000, // 30 seconds
...(useSts ? stsInfo
: {}),
});
return {
uploadId: preInit.uploadId,
};
}
}