@@ -31,7 +31,6 @@ import com.typesafe.scalalogging.LazyLogging
3131import org .apache .commons .io .FileUtils
3232import play .api .libs .json .{Json , OFormat , Reads }
3333import software .amazon .awssdk .auth .credentials .{AwsBasicCredentials , StaticCredentialsProvider }
34- import software .amazon .awssdk .core .async .AsyncRequestBody
3534import software .amazon .awssdk .core .checksums .RequestChecksumCalculation
3635import software .amazon .awssdk .regions .Region
3736import software .amazon .awssdk .services .s3 .S3AsyncClient
@@ -40,9 +39,10 @@ import software.amazon.awssdk.services.s3.model.{
4039 CompletedMultipartUpload ,
4140 CompletedPart ,
4241 CreateMultipartUploadRequest ,
43- PutObjectRequest ,
4442 UploadPartRequest
4543}
44+ import software .amazon .awssdk .transfer .s3 .S3TransferManager
45+ import software .amazon .awssdk .transfer .s3 .model .UploadDirectoryRequest
4646
4747import java .io .{File , RandomAccessFile }
4848import java .net .URI
@@ -584,7 +584,6 @@ class UploadService @Inject()(dataSourceService: DataSourceService,
584584 dataSource,
585585 newBasePath = s " s3:// $endPointHost/ ${dataStoreConfig.Datastore .S3Upload .bucketName}/ $s3ObjectKey" )
586586 _ <- remoteWebknossosClient.updateDataSource(s3DataSource, datasetId, allowNewPaths = true )
587- // TODO: Is uploaded dataset size the same as local dataset size?
588587 datasetSize <- tryo(FileUtils .sizeOfDirectoryAsBigInteger(new File (unpackToDir.toString)).longValue).toFox
589588 _ = this .synchronized {
590589 PathUtils .deleteDirectoryRecursively(unpackToDir)
@@ -671,38 +670,22 @@ class UploadService @Inject()(dataSourceService: DataSourceService,
671670 exploreLocalLayerService.writeLocalDatasourceProperties(dataSource, path))
672671 } yield path
673672
673+ private lazy val transferManager = S3TransferManager .builder().s3Client(s3Client).build()
674+
674675 private def uploadDirectoryToS3 (
675676 dataDir : Path ,
676677 bucketName : String ,
677678 prefix : String
678679 ): Fox [Unit ] =
679680 for {
680- files <- PathUtils .listFilesRecursive(dataDir, silent = false , maxDepth = 20 ).toFox
681- uploadFoxes = files.map(filePath => {
682- val relPath = dataDir.relativize(filePath).toString.replace(" \\ " , " /" )
683- val s3Key = s " $prefix$relPath"
684-
685- // TODO: For large files, consider using multipart upload
686- logger.info(" Uploading file to S3: " + filePath)
687- val bytes = Files .readAllBytes(filePath)
688- logger.info(s " Dataset Upload: Uploading ${bytes.length} bytes to s3:// $bucketName/ $s3Key" )
689- val startTime = System .currentTimeMillis()
690- logger.info(s " Starting upload of $filePath to S3 at $startTime" )
691-
692- for {
693- _ <- Fox .fromFuture {
694- s3Client
695- .putObject(
696- PutObjectRequest .builder().bucket(bucketName).key(s3Key).build(),
697- AsyncRequestBody .fromBytes(bytes)
698- )
699- .asScala
700- } ?~> s " Failed to upload file $filePath to S3 "
701- } yield ()
702- })
703- // TODO: Limit number of concurrent uploads?
704- _ <- Fox .combined(uploadFoxes)
705- _ = logger.info(s " Finished uploading directory to S3 at ${System .currentTimeMillis()}" )
681+ _ <- Fox .successful(())
682+ directoryUpload = transferManager.uploadDirectory(
683+ UploadDirectoryRequest .builder().bucket(bucketName).s3Prefix(prefix).source(dataDir).build()
684+ )
685+ completedUpload <- Fox .fromFuture(directoryUpload.completionFuture().asScala)
686+ failedTransfers = completedUpload.failedTransfers()
687+ _ <- Fox .fromBool(failedTransfers.isEmpty) ?~>
688+ s " Some files failed to upload to S3: $failedTransfers"
706689 } yield ()
707690
708691 private def cleanUpOnFailure [T ](result : Box [T ],
0 commit comments