oss1
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
export interface OssUploadTokenInput {
|
||||
bizType: string;
|
||||
mediaType: string;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export interface OssUploadTokenResult {
|
||||
bucket: string;
|
||||
region: string;
|
||||
ossKey: string;
|
||||
url: string;
|
||||
mock: boolean;
|
||||
expireAt: string;
|
||||
mediaType: string;
|
||||
bizType: string;
|
||||
/** OSS PostObject 直传 host,mock 时为空 */
|
||||
host?: string;
|
||||
/** Base64 policy,mock 时为空 */
|
||||
policy?: string;
|
||||
/** HMAC-SHA1 签名,mock 时为空 */
|
||||
signature?: string;
|
||||
/** RAM AccessKeyId,mock 时为空 */
|
||||
accessKeyId?: string;
|
||||
}
|
||||
|
||||
export interface IOssProvider {
|
||||
isEnabled(): boolean;
|
||||
getUploadToken(input: OssUploadTokenInput): OssUploadTokenResult;
|
||||
buildPublicUrl(ossKey: string): string;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { randomUUID } from 'crypto';
|
||||
import type { IOssProvider, OssUploadTokenInput, OssUploadTokenResult } from './oss.interface';
|
||||
|
||||
@Injectable()
|
||||
export class OssMockProvider implements IOssProvider {
|
||||
private readonly bucket = process.env.OSS_BUCKET || 'mock-dukang';
|
||||
private readonly region = process.env.OSS_REGION || 'oss-cn-hangzhou';
|
||||
private readonly cdnBase = process.env.OSS_CDN_BASE || 'https://mock-cdn.dukang.local';
|
||||
|
||||
isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
buildPublicUrl(ossKey: string) {
|
||||
return `${this.cdnBase.replace(/\/$/, '')}/${ossKey.replace(/^\//, '')}`;
|
||||
}
|
||||
|
||||
getUploadToken(dto: OssUploadTokenInput): OssUploadTokenResult {
|
||||
const ext = dto.fileName.includes('.') ? dto.fileName.split('.').pop() : 'bin';
|
||||
const key = `uploads/${dto.bizType.toLowerCase()}/${Date.now()}-${randomUUID().slice(0, 8)}.${ext}`;
|
||||
return {
|
||||
bucket: this.bucket,
|
||||
region: this.region,
|
||||
ossKey: key,
|
||||
url: this.buildPublicUrl(key),
|
||||
mock: true,
|
||||
expireAt: new Date(Date.now() + 15 * 60 * 1000).toISOString(),
|
||||
mediaType: dto.mediaType,
|
||||
bizType: dto.bizType,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user