feat: 支持了S3协议的文件上传SDK
This commit is contained in:
parent
201f174dd1
commit
246b59bba9
|
|
@ -29,6 +29,8 @@
|
|||
"dependencies": {
|
||||
"@alicloud/dysmsapi20170525": "^2.0.24",
|
||||
"@alicloud/pop-core": "^1.7.12",
|
||||
"@aws-sdk/client-s3": "^3.910.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.910.0",
|
||||
"ali-oss": "^6.20.0",
|
||||
"aws-sdk": "^2.1499.0",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
import { S3Instance } from './service/s3/S3';
|
||||
import { S3Zone } from './types';
|
||||
|
||||
class S3SDK {
|
||||
s3Map: Record<string, S3Instance>;
|
||||
|
||||
constructor() {
|
||||
this.s3Map = {};
|
||||
}
|
||||
|
||||
getInstance(
|
||||
accessKey: string,
|
||||
accessSecret: string,
|
||||
endpoint?: string,
|
||||
region: S3Zone = 'us-east-1'
|
||||
) {
|
||||
// 使用 accessKey + endpoint 作为唯一标识
|
||||
const key = endpoint ? `${accessKey}:${endpoint}` : accessKey;
|
||||
|
||||
if (this.s3Map[key]) {
|
||||
return this.s3Map[key];
|
||||
}
|
||||
|
||||
const instance = new S3Instance(accessKey, accessSecret, endpoint, region);
|
||||
this.s3Map[key] = instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
const SDK = new S3SDK();
|
||||
export default SDK;
|
||||
|
||||
export { S3Instance };
|
||||
|
|
@ -11,6 +11,7 @@ import ALiYunSDK, { ALiYunInstance } from './ALiYunSDK';
|
|||
import TencentYunSDK, { TencentYunInstance } from './TencentYunSDK';
|
||||
import MapwordSDK, { MapWorldInstance } from './MapWorldSDK';
|
||||
import LocalSDK, { LocalInstance } from './LocalSDK';
|
||||
import S3SDK, { S3Instance } from './S3SDK';
|
||||
|
||||
export * from './service/amap/Amap';
|
||||
export {
|
||||
|
|
@ -37,6 +38,9 @@ export {
|
|||
TencentSmsInstance,
|
||||
AliSmsInstance,
|
||||
CTYunSmsInstance,
|
||||
|
||||
S3SDK,
|
||||
S3Instance,
|
||||
};
|
||||
|
||||
export * from './types';
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
import {
|
||||
S3Client,
|
||||
PutObjectCommand,
|
||||
DeleteObjectCommand,
|
||||
HeadObjectCommand,
|
||||
} from '@aws-sdk/client-s3';
|
||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||
import { S3Zone } from '../../types';
|
||||
|
||||
export class S3Instance {
|
||||
private accessKey: string;
|
||||
private secretKey: string;
|
||||
private client: S3Client;
|
||||
private defaultEndpoint?: string;
|
||||
private defaultRegion: string;
|
||||
|
||||
constructor(
|
||||
accessKey: string,
|
||||
secretKey: string,
|
||||
endpoint?: string,
|
||||
region: S3Zone = 'us-east-1'
|
||||
) {
|
||||
this.accessKey = accessKey;
|
||||
this.secretKey = secretKey;
|
||||
this.defaultEndpoint = endpoint;
|
||||
this.defaultRegion = region;
|
||||
|
||||
// 创建默认客户端
|
||||
this.client = this.createClient(endpoint, region);
|
||||
}
|
||||
|
||||
private createClient(endpoint?: string, region: string = this.defaultRegion) {
|
||||
const config: any = {
|
||||
region,
|
||||
credentials: {
|
||||
accessKeyId: this.accessKey,
|
||||
secretAccessKey: this.secretKey,
|
||||
},
|
||||
};
|
||||
|
||||
// 如果指定了 endpoint (Minio 场景)
|
||||
if (endpoint) {
|
||||
let url = endpoint;
|
||||
if (!/^https?:\/\//.test(url)) {
|
||||
url = `http://${url}`; // 自动补协议
|
||||
}
|
||||
if (!url.endsWith('/')) {
|
||||
url += '/'; // 自动补尾斜杠
|
||||
}
|
||||
config.endpoint = url;
|
||||
config.tls = url.startsWith('https');
|
||||
config.forcePathStyle = true; // Minio 通常需要路径风格
|
||||
}
|
||||
|
||||
return new S3Client(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传信息(预签名 URL)
|
||||
*/
|
||||
async getUploadInfo(
|
||||
bucket: string,
|
||||
key: string,
|
||||
endpoint?: string,
|
||||
pathStyle: boolean = false
|
||||
) {
|
||||
try {
|
||||
const client = endpoint
|
||||
? this.createClient(endpoint, this.defaultRegion)
|
||||
: this.client;
|
||||
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
});
|
||||
|
||||
// 生成预签名 URL,有效期 1 小时
|
||||
const uploadUrl = await getSignedUrl(client, command, {
|
||||
expiresIn: 3600,
|
||||
});
|
||||
|
||||
return {
|
||||
key,
|
||||
uploadUrl,
|
||||
bucket,
|
||||
accessKey: this.accessKey,
|
||||
};
|
||||
} catch (err: any) {
|
||||
throw new Error(`生成S3上传URL失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*/
|
||||
async removeFile(
|
||||
bucket: string,
|
||||
key: string,
|
||||
endpoint?: string,
|
||||
pathStyle: boolean = false
|
||||
) {
|
||||
try {
|
||||
const client = endpoint
|
||||
? this.createClient(endpoint, this.defaultRegion)
|
||||
: this.client;
|
||||
|
||||
const command = new DeleteObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
});
|
||||
|
||||
await client.send(command);
|
||||
} catch (err: any) {
|
||||
throw new Error(`删除S3文件失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查对象是否存在
|
||||
*/
|
||||
async isExistObject(
|
||||
bucket: string,
|
||||
key: string,
|
||||
endpoint?: string,
|
||||
pathStyle: boolean = false
|
||||
) {
|
||||
try {
|
||||
const client = endpoint
|
||||
? this.createClient(endpoint, this.defaultRegion)
|
||||
: this.client;
|
||||
|
||||
const command = new HeadObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
});
|
||||
|
||||
await client.send(command);
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
if (err.name === 'NotFound' || err.$metadata?.httpStatusCode === 404) {
|
||||
return false;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件访问 URL
|
||||
*/
|
||||
getFileUrl(
|
||||
bucket: string,
|
||||
key: string,
|
||||
endpoint?: string,
|
||||
pathStyle: boolean = false
|
||||
) {
|
||||
if (endpoint) {
|
||||
// Minio 或自定义 endpoint
|
||||
if (pathStyle) {
|
||||
return `${endpoint}/${bucket}/${key}`;
|
||||
}
|
||||
return `${endpoint.replace(/https?:\/\//, `$&${bucket}.`)}/${key}`;
|
||||
}
|
||||
|
||||
// AWS S3 默认
|
||||
return `https://${bucket}.s3.${this.defaultRegion}.amazonaws.com/${key}`;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// types/S3.ts
|
||||
export type S3Zone =
|
||||
| 'us-east-1'
|
||||
| 'us-east-2'
|
||||
| 'us-west-1'
|
||||
| 'us-west-2'
|
||||
| 'eu-west-1'
|
||||
| 'eu-west-2'
|
||||
| 'eu-west-3'
|
||||
| 'eu-central-1'
|
||||
| 'ap-southeast-1'
|
||||
| 'ap-southeast-2'
|
||||
| 'ap-northeast-1'
|
||||
| 'ap-northeast-2'
|
||||
| 'ap-south-1'
|
||||
| string; // 允许自定义区域
|
||||
|
|
@ -3,4 +3,5 @@ export * from './Qiniu';
|
|||
export * from './CTYun';
|
||||
export * from './ALiYun';
|
||||
export * from './TencentYun';
|
||||
export * from './AMap';
|
||||
export * from './AMap';
|
||||
export * from './S3';
|
||||
Loading…
Reference in New Issue