Play 2.x : Reactive file upload with Iteratees

后端 未结 4 898
北恋
北恋 2020-12-04 10:18

I will start with the question: How to use Scala API\'s Iteratee to upload a file to the cloud storage (Azure Blob Storage in my case, but I don\'t thi

4条回答
  •  没有蜡笔的小新
    2020-12-04 10:53

    If your goal is to stream to S3, here is a helper that I have implemented and tested:

    def uploadStream(bucket: String, key: String, enum: Enumerator[Array[Byte]])
                    (implicit ec: ExecutionContext): Future[CompleteMultipartUploadResult] = {
      import scala.collection.JavaConversions._
    
      val initRequest = new InitiateMultipartUploadRequest(bucket, key)
      val initResponse = s3.initiateMultipartUpload(initRequest)
      val uploadId = initResponse.getUploadId
    
      val rechunker: Enumeratee[Array[Byte], Array[Byte]] = Enumeratee.grouped {
        Traversable.takeUpTo[Array[Byte]](5 * 1024 * 1024) &>> Iteratee.consume()
      }
    
      val uploader = Iteratee.foldM[Array[Byte], Seq[PartETag]](Seq.empty) { case (etags, bytes) =>
        val uploadRequest = new UploadPartRequest()
          .withBucketName(bucket)
          .withKey(key)
          .withPartNumber(etags.length + 1)
          .withUploadId(uploadId)
          .withInputStream(new ByteArrayInputStream(bytes))
          .withPartSize(bytes.length)
    
        val etag = Future { s3.uploadPart(uploadRequest).getPartETag }
        etag.map(etags :+ _)
      }
    
      val futETags = enum &> rechunker |>>> uploader
    
      futETags.map { etags =>
        val compRequest = new CompleteMultipartUploadRequest(bucket, key, uploadId, etags.toBuffer[PartETag])
        s3.completeMultipartUpload(compRequest)
      }.recoverWith { case e: Exception =>
        s3.abortMultipartUpload(new AbortMultipartUploadRequest(bucket, key, uploadId))
        Future.failed(e)
      }
    
    }
    

提交回复
热议问题