This commit is contained in:
2026-07-01 20:21:51 +08:00
parent aea1513836
commit ef6cc18fb2
8 changed files with 178 additions and 20 deletions
@@ -0,0 +1,69 @@
import { createHmac } from 'crypto';
import { Injectable } from '@nestjs/common';
import { randomUUID } from 'crypto';
import type { IOssProvider, OssUploadTokenInput, OssUploadTokenResult } from './oss.interface';
const DEFAULT_EXPIRE_SECONDS = 15 * 60;
const DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
@Injectable()
export class OssAliyunProvider implements IOssProvider {
private readonly accessKeyId = process.env.OSS_ACCESS_KEY_ID ?? '';
private readonly accessKeySecret = process.env.OSS_ACCESS_KEY_SECRET ?? '';
private readonly bucket = process.env.OSS_BUCKET ?? '';
private readonly region = process.env.OSS_REGION ?? 'oss-cn-hangzhou';
private readonly cdnBase = process.env.OSS_CDN_BASE ?? '';
private readonly uploadPrefix = (process.env.OSS_UPLOAD_PREFIX ?? 'uploads').replace(/\/$/, '');
private readonly expireSeconds = Number(process.env.OSS_UPLOAD_EXPIRE_SECONDS ?? DEFAULT_EXPIRE_SECONDS);
private readonly maxUploadBytes = Number(process.env.OSS_MAX_UPLOAD_BYTES ?? DEFAULT_MAX_BYTES);
isEnabled() {
return !!(this.accessKeyId && this.accessKeySecret && this.bucket);
}
buildPublicUrl(ossKey: string) {
const key = ossKey.replace(/^\//, '');
if (this.cdnBase) {
return `${this.cdnBase.replace(/\/$/, '')}/${key}`;
}
return `https://${this.bucket}.${this.region}.aliyuncs.com/${key}`;
}
getUploadToken(dto: OssUploadTokenInput): OssUploadTokenResult {
if (!this.isEnabled()) {
throw new Error('OSS credentials are not configured');
}
const ext = dto.fileName.includes('.') ? dto.fileName.split('.').pop() : 'bin';
const dir = `${this.uploadPrefix}/${dto.bizType.toLowerCase()}/`;
const ossKey = `${dir}${Date.now()}-${randomUUID().slice(0, 8)}.${ext}`;
const expireAt = new Date(Date.now() + this.expireSeconds * 1000);
const host = `https://${this.bucket}.${this.region}.aliyuncs.com`;
const policy = {
expiration: expireAt.toISOString(),
conditions: [
['content-length-range', 0, this.maxUploadBytes],
['eq', '$bucket', this.bucket],
['starts-with', '$key', dir],
],
};
const policyBase64 = Buffer.from(JSON.stringify(policy)).toString('base64');
const signature = createHmac('sha1', this.accessKeySecret).update(policyBase64).digest('base64');
return {
bucket: this.bucket,
region: this.region,
ossKey,
url: this.buildPublicUrl(ossKey),
mock: false,
expireAt: expireAt.toISOString(),
mediaType: dto.mediaType,
bizType: dto.bizType,
host,
policy: policyBase64,
signature,
accessKeyId: this.accessKeyId,
};
}
}