Скачайте архивный файл на лету, используя play framework 2.3 - PullRequest
1 голос
/ 29 мая 2019

Я пытаюсь создать и загрузить файл архива, не полагаясь на память (чтобы избежать исключения нехватки памяти для большого файла), который я использую для этой платформы воспроизведения 2.3 с Scala, после некоторого исследования я нашел пример: https://gist.github.com/kirked/412b5156f94419e71ce4a84ec1d54761, Я сделал некоторые изменения на нем. Моя проблема в том, что когда я загружаю файл и пытаюсь открыть его, я получаю следующее исключение: Произошла ошибка при загрузке архива. здесь весь код:

 def zip() = Action {
      implicit request: Request[AnyContent] =>
      val buffer = new ZipBuffer(10000)

      val writeCentralDirectory = Enumerator.generateM(Future{
        if (buffer.isClosed) {
          None
        }
        else {
          buffer.flush
          buffer.close
          Some(buffer.bytes)
        }
      })

      val test = Enumerator.apply(ResolvedSource2("test.txt", "helllo"))    

      Ok.chunked(test &> zipeach2(buffer) andThen writeCentralDirectory >>> Enumerator.eof) as withCharset("application/zip") withHeaders(
        CONTENT_DISPOSITION -> s"attachment; filename=aa.zip")
  }
  case class ResolvedSource2(filepath: String, stream: String)
  def zipeach2(buffer: ZipBuffer)(implicit ec: ExecutionContext): Enumeratee[ResolvedSource2, Array[Byte]] = {
    Enumeratee.mapConcat[ResolvedSource2] { source =>
      buffer.zipStream.putNextEntry(new ZipEntry(source.filepath))
       var done = false
      def entryDone: Unit = {
        done = true
        buffer.zipStream.closeEntry
      }
      def restOfStream: Stream[Array[Byte]] = {
        if (done) Stream.empty
        else {
           while (!done && !buffer.full) {
          try {
            val byte = source.stream
            buffer.zipStream.write(byte.getBytes)
           entryDone
          }
          catch {
            case e: IOException =>
              println(s"reading/zipping stream [${source.filepath}]", e)
          }
           }
          buffer.bytes #:: restOfStream
        }
      }
      restOfStream
    }
  }
}

class ZipBuffer(capacity: Int) {
  private val buf = new ByteArrayOutputStream(capacity)
  private var closed = false
  val zipStream = new ZipOutputStream(buf)
  def close(): Unit = {

    if (!closed) {
      closed = true
      reset
      zipStream.finish()
      zipStream.close   // writes central directory
    }
  }
  def flush() = {
    zipStream.flush()
  }

  def isClosed = closed

  def reset: Unit = buf.reset

  def full: Boolean = buf.size >= capacity

  def bytes: Array[Byte] = {
    val result = buf.toByteArray
    reset
    result
  }
} 
...