feat: 支持了S3协议的文件上传SDK

This commit is contained in:
Pan Qiancheng 2025-10-15 16:29:34 +08:00
parent 201f174dd1
commit 246b59bba9
6 changed files with 225 additions and 1 deletions

View File

@ -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",

34
src/S3SDK.ts Normal file
View File

@ -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 };

View File

@ -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';

167
src/service/s3/S3.ts Normal file
View File

@ -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}`;
}
}

16
src/types/S3.ts Normal file
View File

@ -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; // 允许自定义区域

View File

@ -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';