| 52 | * 使用原生 fetch + AWS Signature V4 签名,不依赖 @aws-sdk/client-s3 |
| 53 | */ |
| 54 | export default class S3FileSystem implements FileSystem { |
| 55 | client: S3Client; |
| 56 | |
| 57 | bucket: string; |
| 58 | |
| 59 | basePath: string = "/"; |
| 60 | |
| 61 | constructor(bucket: string, client: S3Client, basePath?: string); |
| 62 | constructor( |
| 63 | bucket: string, |
| 64 | region: string, |
| 65 | accessKeyId: string, |
| 66 | secretAccessKey: string, |
| 67 | endpoint?: string, |
| 68 | basePath?: string |
| 69 | ); |
| 70 | constructor( |
| 71 | bucket: string, |
| 72 | regionOrClient: string | S3Client, |
| 73 | accessKeyIdOrBasePath?: string, |
| 74 | secretAccessKey?: string, |
| 75 | endpoint?: string, |
| 76 | basePath?: string |
| 77 | ) { |
| 78 | this.bucket = bucket; |
| 79 | if (regionOrClient instanceof S3Client) { |
| 80 | this.client = regionOrClient; |
| 81 | this.basePath = accessKeyIdOrBasePath || "/"; |
| 82 | return; |
| 83 | } |
| 84 | this.basePath = basePath || "/"; |
| 85 | |
| 86 | const config: S3ClientConfig = { |
| 87 | region: regionOrClient || "us-east-1", |
| 88 | credentials: { |
| 89 | accessKeyId: accessKeyIdOrBasePath!, |
| 90 | secretAccessKey: secretAccessKey!, |
| 91 | }, |
| 92 | forcePathStyle: true, // 强制路径式访问,兼容大多数 S3 服务 |
| 93 | }; |
| 94 | |
| 95 | if (endpoint) { |
| 96 | let fixedEndpoint = `${endpoint}`.trim(); |
| 97 | if (!fixedEndpoint.startsWith("http://") && !fixedEndpoint.startsWith("https://")) { |
| 98 | fixedEndpoint = `https://${fixedEndpoint}`; |
| 99 | } |
| 100 | config.endpoint = fixedEndpoint; |
| 101 | // amazonaws.com 域名使用虚拟主机风格 |
| 102 | if (endpoint.includes("amazonaws.com")) config.forcePathStyle = false; |
| 103 | } |
| 104 | |
| 105 | this.client = new S3Client(config); |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * 验证 Bucket 访问权限和凭证 |
| 110 | * @throws {WarpTokenError} 认证失败 |
| 111 | * @throws {Error} Bucket 不存在或网络错误 |
nothing calls this directly
no outgoing calls
no test coverage detected