1

I'am trying to create and download an archive file without relying on memory(to avoid out of memory exception for large file) I'am using for that play framework 2.3 with Scala, after some research I found an example: https://gist.github.com/kirked/412b5156f94419e71ce4a84ec1d54761, I made some modification on it. My problem is when I download the file and try to open it I get this exception: An error occurred while loading the archive. here all the code:

 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
  }
} 
med10
  • 101
  • 7

0 Answers0