【问题标题】:How to stream zipped file (on the fly) via Play Framework 2.5 in scala?如何通过 Scala 中的 Play Framework 2.5 流式传输压缩文件(即时)?
【发布时间】:2017-11-06 15:58:14
【问题描述】:

我想流式传输一些文件并即时压缩它们,这样用户就可以将多个文件下载到一个压缩文件中,而无需将任何内容写入本地磁盘。但是,我当前的实现将所有内容都保存在内存中,并且不适用于大文件。有什么办法可以解决吗?

我正在查看这个实现:https://gist.github.com/kirked/03c7f111de0e9a1f74377bf95d3f0f60,但不知道如何使用它。

 import java.io.{BufferedOutputStream, ByteArrayInputStream, ByteArrayOutputStream}
import java.util.zip.{ZipEntry, ZipOutputStream}
import akka.stream.scaladsl.{StreamConverters}
import org.apache.commons.io.FileUtils
import play.api.mvc.{Action, Controller}

class HomeController extends Controller {
  def single() = Action {
                         Ok.sendFile(
                           content = new java.io.File("C:\\Users\\a.csv"),
                           fileName = _ => "a.csv"
                         )
                       }

  def zip() = Action {
                     Ok.chunked(StreamConverters.fromInputStream(fileByteData)).withHeaders(
                       CONTENT_TYPE -> "application/zip",
                       CONTENT_DISPOSITION -> s"attachment; filename = test.zip"
                     )
                   }

  def fileByteData(): ByteArrayInputStream = {
    val fileList = List(
      new java.io.File("C:\\Users\\a.csv"),
      new java.io.File("C:\\Users\\b.csv")
    )

    val baos = new ByteArrayOutputStream()
    val zos = new ZipOutputStream(new BufferedOutputStream(baos))

    try {
      fileList.map(file => {
        zos.putNextEntry(new ZipEntry(file.toPath.getFileName.toString))
        zos.write(FileUtils.readFileToByteArray(file))
        zos.closeEntry()
      })
    } finally {
      zos.close()
    }

    new ByteArrayInputStream(baos.toByteArray)
  }
}

【问题讨论】:

    标签: scala playframework zip akka akka-stream


    【解决方案1】:

    您可以使用 Java 的管道机制,而不是使用 ByteArrayOutputStream 来缓冲数组中的内容,然后将它们放入 ByteArrayInputStream

    这是一个草图解决方案:

    def zip() = Action {
      // Create Source that listens to an OutputStream
      // and pass it to `fileByteData` method.
      val zipSource: Source[ByteString, Unit] =
        StreamConverters
          .asOutputStream()
          .mapMaterializedValue(fileByteData)
      Ok.chunked(zipSource).withHeaders(
        CONTENT_TYPE -> "application/zip",
        CONTENT_DISPOSITION -> s"attachment; filename = test.zip")
    }
    
    // Send the file data, given an OutputStream to write to.
    def fileByteData(os: OutputStream): Unit = {
      val fileList = List(
        new java.io.File("C:\\Users\\a.csv"),
        new java.io.File("C:\\Users\\b.csv")
      )
    
      val zos = new ZipOutputStream(os)
      val buffer: Array[Byte] = new Array[Byte](2048)
      try {
        for (file <- fileList) {
          zos.putNextEntry(new ZipEntry(file.toPath.getFileName.toString))
          val fis = new Files.newInputStream(file.toPath)
          try {
            @tailrec
            def zipFile(): Unit = {
              val bytesRead = fis.read(buffer)
              if (bytesRead == -1) () else {
                zos.write(buffer, 0, bytesRead)
                zipFile()
              }
            }
            zipFile()
          } finally fis.close()
          zos.closeEntry()
        }
      } finally {
        zos.close()
      }
    }
    

    这只是一种方法的概述。您还需要确保: - 线程正常 - fileByteData 有望在与发送线程不同的线程上运行 - 错误处理正常 - 例如如果服务器(例如未找到文件)或客户端(提前断开连接)出现错误,所有流都会正确关闭

    【讨论】:

    • 非常感谢!在“Source.asOutputStream().mapMaterializedValue(fileByteData)”中将“Source”更改为“StreamConverts”可以为我编译。但是,通过这个实现,我得到了 IllegalStateException。使用字节数组缓冲区读取文件后,非法状态异常消失,但文件从未下载..
    • 好的,我已经更新了 sn-p 以便它编译。 IllegalStateException 抛出了什么?
    • 它是“zos.write(FileUtils.readFileToByteArray(file))”。在我用“val buffer: Array[Byte] = new Array[Byte](2048) val bis = new BufferedInputStream(Files.newInputStream(file.toPath)) var bytesRead = bis.read(buffer) while (bytesRead ! = -1) { zos.write(buffer, 0, bytesRead) bytesRead = bis.read(buffer) }" 异常消失了。但是zip文件夹没有下载。
    • 糟糕,完全忘记了FileUtils.readFileToByteArray(file) 使用缓冲区将每个文件读入内存。我已经部分根据您的 while 循环更新了代码。你想检查一下吗?还有哪个班级在扔IllegalStateException
    • OK - 现在异常消失了吗?文件正在下载吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 2010-11-07
    • 1970-01-01
    • 2017-10-31
    相关资源
    最近更新 更多