如何用Scala下载S3存储桶中全部文件?获取文件而非目录
解决Scala批量下载S3文件的问题
老哥,我太懂你这个痛点了——S3所谓的「目录」其实都是前缀模拟出来的,很多时候直接调用list接口会把这些前缀当成条目返回,导致你拿不到真正的文件列表。下面给你一套完整的Scala解决方案,帮你搞定批量下载:
第一步:确认依赖
首先确保你的项目里引入了AWS SDK v2的S3依赖(v1已经过时,不推荐用了),如果是用SBT的话,在build.sbt里加:
libraryDependencies += "software.amazon.awssdk" % "s3" % "2.20.0" libraryDependencies += "software.amazon.awssdk" % "regions" % "2.20.0"
第二步:获取所有真实文件对象
这个方法会自动处理分页(避免文件太多一次拉取不到),并且过滤掉那些模拟目录的前缀(也就是key以/结尾的条目):
import software.amazon.awssdk.regions.Region import software.amazon.awssdk.services.s3.S3Client import software.amazon.awssdk.services.s3.model.{ListObjectsV2Request, ListObjectsV2Response, S3Object} def listAllS3Objects(bucketName: String, prefix: String = ""): List[S3Object] = { // 替换成你的S3桶所在区域 val s3Client = S3Client.builder().region(Region.US_EAST_1).build() var continuationToken: String = null var allObjects = List.empty[S3Object] do { val request = ListObjectsV2Request.builder() .bucket(bucketName) .prefix(prefix) // 可选:指定前缀,只遍历该前缀下的文件 .continuationToken(continuationToken) .build() val response: ListObjectsV2Response = s3Client.listObjectsV2(request) // 过滤掉模拟目录,只保留真实文件 val validObjects = response.contents().stream() .filter(obj => !obj.key().endsWith("/")) .toList() allObjects = allObjects ++ validObjects continuationToken = response.nextContinuationToken() } while (continuationToken != null) s3Client.close() allObjects }
第三步:批量下载文件
结合你已经能正常工作的单个文件下载逻辑,我们封装一个批量下载的方法,还会自动在本地创建对应的目录结构:
import java.nio.file.Paths import software.amazon.awssdk.services.s3.model.GetObjectRequest // 单个文件下载(可以复用你已有的逻辑,这里是标准实现) def downloadSingleS3File(s3Client: S3Client, bucketName: String, objectKey: String, localRoot: String): Unit = { val getRequest = GetObjectRequest.builder() .bucket(bucketName) .key(objectKey) .build() // 根据S3的key在本地创建对应的路径,保持层级结构 val localFilePath = Paths.get(localRoot, objectKey) // 自动创建父目录(如果不存在) java.nio.file.Files.createDirectories(localFilePath.getParent) // 下载文件到本地 s3Client.getObject(getRequest, localFilePath) } // 批量下载入口方法 def batchDownloadFromS3(bucketName: String, prefix: String = "", localRootPath: String): Unit = { val s3Client = S3Client.builder().region(Region.US_EAST_1).build() val allFiles = listAllS3Objects(bucketName, prefix) println(s"找到 ${allFiles.size} 个待下载文件...") allFiles.foreach { file => println(s"正在下载:${file.key()}") downloadSingleS3File(s3Client, bucketName, file.key(), localRootPath) } s3Client.close() println("所有文件下载完成!") }
关键注意点
- 区域替换:把代码里的
Region.US_EAST_1换成你的S3桶实际所在的区域(比如Region.AP_SOUTHEAST_1对应新加坡区)。 - 凭证配置:确保你的程序能拿到AWS凭证——可以通过环境变量、
~/.aws/credentials文件,或者如果是在EC2/EKS上运行的话,直接用IAM角色。 - 前缀控制:如果只需要下载桶里某个「目录」下的文件,给
prefix参数传对应的路径(比如"reports/2024/"),就能只遍历该前缀下的文件。
内容的提问来源于stack exchange,提问作者Aryan Singh
相关产品推荐
相关产品推荐

