From 7954516ca2047e2ca5f652a335815042bf7fc930 Mon Sep 17 00:00:00 2001 From: lihsai0 Date: Thu, 30 May 2024 21:28:51 +0800 Subject: [PATCH] feat: add style for get object url --- adapter.ts | 8 +++++++- kodo.ts | 5 +++++ s3.ts | 34 +++++++++++++++++++++++++--------- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/adapter.ts b/adapter.ts index 874abcd..25b3fd6 100644 --- a/adapter.ts +++ b/adapter.ts @@ -32,7 +32,13 @@ export abstract class Adapter { abstract getObjectInfo(region: string, object: StorageObject): Promise; abstract getObjectHeader(region: string, object: StorageObject, domain?: Domain): Promise; abstract getObject(region: string, object: StorageObject, domain?: Domain): Promise; - abstract getObjectURL(region: string, object: StorageObject, domain?: Domain, deadline?: Date): Promise; + abstract getObjectURL( + region: string, + object: StorageObject, + domain?: Domain, + deadline?: Date, + style?: 'path' | 'virtualHost' | 'bucketEndpoint' + ): Promise; abstract getObjectStream(s3RegionId: string, object: StorageObject, domain?: Domain, option?: GetObjectStreamOption): Promise; abstract putObject( region: string, diff --git a/kodo.ts b/kodo.ts index 9e9ffb9..c1d633c 100644 --- a/kodo.ts +++ b/kodo.ts @@ -604,6 +604,7 @@ export class Kodo implements Adapter { object: StorageObject, domain?: Domain, deadline?: Date, + style: 'path' | 'virtualHost' | 'bucketEndpoint' = 'bucketEndpoint', ): Promise { if (!domain) { let domains = await this._listDomains(s3RegionId, object.bucket); @@ -613,6 +614,10 @@ export class Kodo implements Adapter { domain = domains[0]; } + if (style !== 'bucketEndpoint') { + throw new Error('Only support "bucketEndpoint" style for now'); + } + let url = new URL(`${domain.protocol}://${domain.name}`); url.pathname = encodeURI(object.key); if (domain.private || domain.protected) { diff --git a/s3.ts b/s3.ts index a3226cb..5aaa7f0 100644 --- a/s3.ts +++ b/s3.ts @@ -66,8 +66,8 @@ export class S3 extends Kodo { protected clientsLock = new AsyncLock(); protected listKodoBucketsPromise?: Promise; - private async getClient(s3RegionId?: string): Promise { - const cacheKey = s3RegionId ?? ''; + private async getClient(s3RegionId?: string, s3ForcePathStyle = true): Promise { + const cacheKey = [s3RegionId ?? '', s3ForcePathStyle ? 's3ForcePathStyle' : ''].join(':'); if (this.clients[cacheKey]) { return this.clients[cacheKey]; } @@ -98,7 +98,8 @@ export class S3 extends Kodo { agent: s3IdEndpoint.s3Endpoint.startsWith('https://') ? HttpClient.httpsKeepaliveAgent : HttpClient.httpKeepaliveAgent, - } + }, + s3ForcePathStyle }); }); this.clients[cacheKey] = client; @@ -624,10 +625,20 @@ export class S3 extends Kodo { ); } - async getObjectURL(s3RegionId: string, object: StorageObject, domain?: Domain, deadline?: Date): Promise { + async getObjectURL( + s3RegionId: string, + object: StorageObject, + domain?: Domain, + deadline?: Date, + style: 'path' | 'virtualHost' | 'bucketEndpoint' = 'path', + ): Promise { + let s3Promise: Promise; // if domain is not undefined, use the domain, else use the default s3 endpoint - const s3Promise: Promise = domain - ? Promise.resolve(new AWS.S3({ + if (domain) { + if (style !== 'bucketEndpoint') { + throw new Error('Custom S3 endpoint only support "bucketEndpoint" style'); + } + s3Promise = Promise.resolve(new AWS.S3({ apiVersion: '2006-03-01', region: s3RegionId, endpoint: `${domain.protocol}://${domain.name}`, @@ -636,9 +647,14 @@ export class S3 extends Kodo { secretAccessKey: this.adapterOption.secretKey, }, signatureVersion: 'v4', - s3BucketEndpoint: true, - })) - : this.getClient(s3RegionId); + s3BucketEndpoint: true, // use bucketEndpoint style + })); + } else { + if (style === 'bucketEndpoint') { + throw new Error('Default S3 endpoint not support "bucketEndpoint" style'); + } + s3Promise = this.getClient(s3RegionId, style === 'path'); + } const [s3, bucketId] = await Promise.all([ s3Promise, this.fromKodoBucketNameToS3BucketId(object.bucket),