我有两个使用不同帐户的S3存储桶。我想将文件从存储桶A移到存储桶B,并且我想尽快执行此操作。为此,我认为我将使用Scala,异步请求和并行处理来尽可能快地移动文件。
为此,我必须调用listObjects命令(该命令返回其自身的未来,然后对于该命令返回的每个对象,我必须依次运行getObject和putObject。因此,listObjects应该生成几个getObject期货,当这些期货解析时,应该在它们之后放置putObject期货。
我一直试图以这种方式实现这一目标:
def moveData(listObjects: Future[ListObjectV2Response]) = {
listObjects.isCompleted {
case Success(objListResp) =>
val getAndPut = objListResp.objects()
.map(obj => getObject(obj.key))
.map(obj => putObject(obj.key))
moveData(ListObjects(objlistResp.continuationToken())
case Failure(error) => error.printStackTrace()
}
我已经尝试了这种方式的大约6种不同方法。我始终对以下现象感到困惑:
.isCompleted
具有Unit响应类型,而我无法使用递归函数。 .map
链下。.contents
返回一个可迭代的对象,必须将其转换为许多不同的未来。结合(2)会导致对map
和flatMap
的一些非常棘手的使用,其中当实际响应类型为{{1时,scala希望map / flatmap类似于Iterator[S3Object] => Future[NotInferred]
}} 如何解决此问题?有没有更好的办法?
编辑:
有成千上万个文件,其中许多文件的大小为几GB。总的来说,要复制的数据以十亿兆字节为单位。我对源存储桶及其所在的帐户具有极其有限的访问权限。除Get和List操作外,我将无法执行任何其他操作。
答案 0 :(得分:2)
如果两个存储桶位于不同的地区,则可以使用Amazon S3 Cross-Region Replication。
如果它们位于相同区域,则复制对象的“最快”方法是:
CopyObject()
命令将对象复制到另一个存储桶 此方法的优点是无需列出存储桶中的对象,因为将为每个新对象触发Lambda函数。
答案 1 :(得分:0)
尽管遇到了并发问题导致的错误,但我设法编写了一个在技术上可以运行的极其粗糙的函数。在这里张贴以供后代使用,但我不喜欢这个答案。
def streamData(response: Future[ListObjectsV2Response]): Future[ListObjectsV2Response] = {
var continuationToken: String = ""
val operationChain = response.map((res: ListObjectsV2Response) => {
println("LISTED - " + res.maxKeys())
continuationToken = res.nextContinuationToken()
res.contents().asScala.toList
}).map((objs: List[S3Object]) => {
for {
obj <- objs
fileName = obj.key().split("/").last
getObjectRequest = GetObjectRequest.builder().bucket(BucketA).key(obj.key()).build()
writeFilePath = Paths.get("./" + fileName)
future = BucketAS3.getObject(getObjectRequest, writeFilePath).toScala
} yield {
(future, Future(obj.key()), Future(writeFilePath))
}
}).map((futureSeq: Seq[(Future[GetObjectResponse], Future[String], Future[Path])]) =>
futureSeq.map((futureTuple: (Future[GetObjectResponse], Future[String], Future[Path])) => {
for {
getObjResp <- futureTuple._1
key <- futureTuple._2
writeFilePath <- futureTuple._3
} yield {
println("DOWNLOADED - " + key)
val putObjectRequest = PutObjectRequest.builder()
.bucket(bucketB).key("ability_dump/" + key).build()
(BucketBS3.putObject(putObjectRequest, writeFilePath).toScala, Future(key), Future(writeFilePath))
}
})
).map((futuresSeq: Seq[Future[(Future[PutObjectResponse], Future[String], Future[Path])]]) => {
futuresSeq.map((futures: Future[(Future[PutObjectResponse], Future[String], Future[Path])]) => {
for {
f <- futures
putObjResp <- f._1
key <- f._2
writeFilePath <- f._3
} yield {
println("UPLOADED - " + key)
val writeFile = new File(writeFilePath.toString)
if (writeFile.exists) {
writeFile.delete()
}
println("DELETED - " + writeFilePath.toString)
}
})
})
streamData(BucketAS3.listObjectsV2(abilityListRequestBuilder.continuationToken(continuationToken).build()).toScala)
}