@@ -58,6 +58,12 @@ export default class S3FileSystem implements FileSystem {
5858 } ;
5959
6060 if ( endpoint ) {
61+ // AWS SDK v3 要求 endpoint 是完整 URL(带 https://)。缺少协议会导致 InvalidEndpoint 或连接失败。
62+ // 自动补全协议(常见兼容做法)
63+ let fixedEndpoint = `${ endpoint } ` . trim ( ) ;
64+ if ( ! fixedEndpoint . startsWith ( "http://" ) && ! fixedEndpoint . startsWith ( "https://" ) ) {
65+ fixedEndpoint = `https://${ fixedEndpoint } ` ;
66+ }
6167 config . endpoint = endpoint ;
6268 // 自动检测:如果 endpoint 包含 amazonaws.com,设为 false。
6369 if ( endpoint . includes ( "amazonaws.com" ) ) config . forcePathStyle = false ;
@@ -78,18 +84,27 @@ export default class S3FileSystem implements FileSystem {
7884 } ) ;
7985 await this . client . send ( command ) ;
8086 } catch ( error : any ) {
87+ if ( error . name === "NotFound" ) {
88+ throw new Error ( "NotFound" ) ; // Bucket 不存在
89+ }
8190 if (
8291 error . name === "InvalidAccessKeyId" ||
8392 error . name === "SignatureDoesNotMatch" ||
8493 error . name === "InvalidClientTokenId"
8594 ) {
86- throw new WarpTokenError ( error ) ;
95+ throw new WarpTokenError ( error ) ; // AccessKey 或 SecretKey 无效
96+ }
97+ if ( error . name === "AccessDenied" ) {
98+ throw new Error ( "Access Denied" ) ; // 无权限访问该 Bucket(Access Denied)
99+ }
100+ if ( error . name === "PermanentRedirect" ) {
101+ throw new Error ( "Access Denied" ) ; // Region 设置错误,请检查 region 是否匹配 Bucket 所在区域
87102 }
88103 if ( error . name === "NoSuchBucket" ) {
89104 throw new Error ( `Bucket not found: ${ this . bucket } ` ) ;
90105 }
91106 if ( error . message ?. includes ( "getaddrinfo" ) || error . message ?. includes ( "fetch failed" ) ) {
92- throw new Error ( "Network connection failed. Please check your internet connection." ) ;
107+ throw new Error ( "Network connection failed. Please check your internet connection." ) ; // 网络连接失败,请检查 endpoint 或网络
93108 }
94109 throw error ;
95110 }
@@ -115,10 +130,11 @@ export default class S3FileSystem implements FileSystem {
115130 const fs = new S3FileSystem (
116131 this . bucket ,
117132 this . region ,
118- "" , // These won't be used since we're reusing the client
133+ "" , // 占位: These won't be used since we're reusing the client
119134 "" ,
120135 undefined ,
121- newBasePath
136+ newBasePath ,
137+ true // forcePathStyle 同父
122138 ) ;
123139 // openDir 又 new 一个 S3FileSystem,但传了空字符串的 AK/SK,导致逻辑不清晰。
124140 // 复用原有 client,只修改 basePath。
@@ -187,25 +203,37 @@ export default class S3FileSystem implements FileSystem {
187203 Prefix : prefix ,
188204 Delimiter : "/" ,
189205 ContinuationToken : continuationToken ,
206+ MaxKeys : 1000 , // ← 每次最多 1000,防滥用
190207 } ) ;
191208
192209 const response = await this . client . send ( command ) ;
193210
211+ // 处理 CommonPrefixes(子目录)
212+ // if (response.CommonPrefixes) {
213+ // for (const prefix of response.CommonPrefixes) {
214+ // if (prefix.Prefix && prefix.Prefix !== this.basePath) {
215+ // }
216+ // }
217+ // }
218+
219+ // 处理 Contents(文件)
194220 if ( response . Contents ) {
195- for ( const object of response . Contents ) {
196- if ( ! object . Key ) continue ;
221+ for ( const obj of response . Contents ) {
222+ if ( ! obj . Key ) continue ;
223+ if ( obj . Key && obj . Key . endsWith ( "/" ) ) continue ; // 跳过目录占位符
224+ if ( obj . Key === this . basePath . slice ( 1 ) ) continue ; // 跳过 prefix 本身
197225
198- // Skip the directory marker itself
199- if ( object . Key === prefix || object . Key . endsWith ( "/" ) ) continue ;
226+ // const name = obj.Key.substring(prefix.length);
227+ const relativeKey = obj . Key . slice ( this . basePath . length ) ;
228+ if ( ! relativeKey ) continue ;
200229
201- const name = object . Key . substring ( prefix . length ) ;
202- const lastModified = object . LastModified ?. getTime ( ) || Date . now ( ) ;
230+ const lastModified = obj . LastModified ?. getTime ( ) || Date . now ( ) ;
203231
204232 files . push ( {
205- name,
233+ name : relativeKey ,
206234 path : this . basePath ,
207- size : object . Size || 0 ,
208- digest : object . ETag ?. replace ( / " / g, "" ) || "" ,
235+ size : obj . Size || 0 ,
236+ digest : obj . ETag ?. replace ( / " / g, "" ) || "" ,
209237 createtime : lastModified ,
210238 updatetime : lastModified ,
211239 } ) ;
@@ -215,6 +243,10 @@ export default class S3FileSystem implements FileSystem {
215243 continuationToken = response . NextContinuationToken ;
216244 } while ( continuationToken ) ;
217245
246+ if ( files . length > 10000 ) {
247+ console . warn ( `Directory listing truncated: >10000 items under ${ this . basePath } ` ) ;
248+ }
249+
218250 return files ;
219251 } catch ( error : any ) {
220252 if ( error . name === "AccessDenied" ) {
0 commit comments