Фрагментированный результат от ZipOutputStream - PullRequest
0 голосов
/ 04 ноября 2019

Я сейчас пытаюсь собрать и отправить zip-архив с Play framework 2.7. Я хотел бы собрать архив на лету, чтобы не использовать хранилище (или заполнять оперативную память).

Я нашел хорошее решение с Enumerator , но с текущей версией Play Framework мне нужно собратьAkka Source.

Моя текущая попытка:

val streamContent = StreamConverters
  .asOutputStream()
  .mapMaterializedValue( ZipUtil.zip(ds.files) )

Ok.chunked(streamContent)
  .as("application/zip")
  .withHeaders( CONTENT_DISPOSITION -> "attachment;" )

, где ZipUtil.zip(...) определяется как:

def zip(files: Iterable[Path]): OutputStream => Unit = { os =>
  val zipOut = new ZipOutputStream(os)
  val bytesBuffer = ByteBuffer.allocate(8192)
  files.foreach { f => 
    val fileName = f.getFileName.toString
    val ze = new ZipEntry(fileName)
    zipOut.putNextEntry(ze)
    val channel = Files.newByteChannel(f, StandardOpenOption.READ)
    bytesBuffer.clear()
    var numBytes = channel.read(bytesBuffer)
    while (numBytes != -1) {
      val bytes = bytesBuffer.array()
      zipOut.write(bytes, 0, numBytes)
      bytesBuffer.clear()
      numBytes = channel.read(bytesBuffer)
    }            
    channel.close()
  }    
  zipOut.close()
}

Однако, похоже, что это тупик. Если я добавлю расширенное ведение журнала, оно застревает при вызове putNextEntry. Конечно, я тестировал метод zip(...) вне механизма обработки потоков akka, и он хорошо работал.

Я также пытался использовать StreamConverters.fromInputStream и использовать потоковые потоки ввода-вывода для подключения ZipOutputStream к Source 1022 * безуспешно.

...