* 发送 S3 请求 * @param method HTTP 方法 * @param bucket Bucket 名称 * @param key 对象 Key(可选) * @param options 请求选项 * @returns 成功时返回 Response * @throws {S3Error} S3 服务端错误
(
method: string,
bucket: string,
key?: string,
options?: {
queryParams?: Record<string, string>;
body?: string | Uint8Array;
headers?: Record<string, string>;
}
)
| 269 | * @throws {S3Error} S3 服务端错误 |
| 270 | */ |
| 271 | async request( |
| 272 | method: string, |
| 273 | bucket: string, |
| 274 | key?: string, |
| 275 | options?: { |
| 276 | queryParams?: Record<string, string>; |
| 277 | body?: string | Uint8Array; |
| 278 | headers?: Record<string, string>; |
| 279 | } |
| 280 | ): Promise<Response> { |
| 281 | const queryParams = options?.queryParams || {}; |
| 282 | // 规范化 headers(全部小写 key) |
| 283 | const headers: Record<string, string> = {}; |
| 284 | if (options?.headers) { |
| 285 | for (const [k, v] of Object.entries(options.headers)) { |
| 286 | headers[k.toLowerCase()] = v; |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | // 计算 payload SHA-256 |
| 291 | let payloadHash: string; |
| 292 | if (options?.body) { |
| 293 | const bodyBytes = typeof options.body === "string" ? new TextEncoder().encode(options.body) : options.body; |
| 294 | payloadHash = await sha256Hex(bodyBytes); |
| 295 | } else { |
| 296 | payloadHash = await sha256Hex(""); |
| 297 | } |
| 298 | |
| 299 | // 签名 |
| 300 | await this.signRequest(method, bucket, key, queryParams, headers, payloadHash); |
| 301 | |
| 302 | // 发送请求(移除 host 头,fetch 会自动设置) |
| 303 | const url = this.buildUrl(bucket, key, queryParams); |
| 304 | const fetchHeaders = { ...headers }; |
| 305 | delete fetchHeaders["host"]; |
| 306 | |
| 307 | const response = await fetch(url, { |
| 308 | method, |
| 309 | headers: fetchHeaders, |
| 310 | body: options?.body |
| 311 | ? options.body instanceof Uint8Array |
| 312 | ? (options.body.buffer as ArrayBuffer) |
| 313 | : options.body |
| 314 | : undefined, |
| 315 | }); |
| 316 | |
| 317 | // 非 2xx 响应视为错误 |
| 318 | if (!response.ok) { |
| 319 | throw await parseS3Error(response); |
| 320 | } |
| 321 | |
| 322 | return response; |
| 323 | } |
| 324 | |
| 325 | /** 获取 endpoint URL */ |
| 326 | getEndpointUrl(): string { |
no test coverage detected