【问题标题】:managed (ARM) in scala for nested resourcesScala 中用于嵌套资源的托管(ARM)
【发布时间】:2017-04-04 23:17:24
【问题描述】:

所以,我在 scala 中有这段代码,我将其转换为托管代码。

val file_out = new FileOutputStream(new java.io.File(filePath, resultFile + ".tar.gz"));
val buffer_out = new BufferedOutputStream(file_out);
val gzip_out = new GzipCompressorOutputStream(buffer_out);
val tar_out = new TarArchiveOutputStream(gzip_out);

try {
    addFileToTarGz(tar_out, filePath + "/" + resultFolder, "");
} finally {
    tar_out.finish();
    tar_out.close();
    gzip_out.close();
    buffer_out.close();
    file_out.close();
}

第一次尝试是:

val file = new java.io.File(filePath, s"$resultFile.tar.gz")
managed(new FileOutputStream(file))
        .acquireAndGet(stream => managed(new BufferedOutputStream(stream))
                .acquireAndGet(buffer => managed(new GzipCompressorOutputStream(buffer))
                        .acquireAndGet(gzip => managed(new TarArchiveOutputStream(gzip))
                                .acquireAndGet(tar => {
                                  try {
                                    addFileToTarGz(tar, filePath + "/" + resultFolder, "")
                                  } finally {
                                    tar.finish()
                                  }
                                }))))

但是,它看起来不太可读。有没有更好的方法让它managed 又可读?

【问题讨论】:

  • 最好使用带有单子ManagedResource的for-comprehension
  • @cchantep 你能在下面评论我的回答吗?这是你建议的吗?
  • (for { a <- managed(res1); b <- managed(res2) } yield a -> b).acquireFor { case (a, b) => compute(a, b) }

标签: scala scala-arm


【解决方案1】:

您考虑过负载模式吗?

def withResource[T](block: Resource => T): T = {
  val resource = new Resource
  try {
    block(resource)
  } finally {
    resource.close()
  }
}

那么你会像这样使用它:

withResourse { resource =>
  // do something with resource
}

如果您对这些文件中的每一个使用单独的加载,您最终会得到嵌套块...(在某些情况下这可能是最合理的选择),但在这里我想这样做就足够了:

def withTarStream(filePath: String, resultFile: String)(block: TarArchiveOutputStream => T): T = {
  val fileOut = new FileOutputStream(new java.io.File(filePath, resultFile))
  val bufferOut = new BufferedOutputStream(fileOut)
  val gzipOut = new GzipCompressorOutputStream(bufferOut)
  val tarOut = new TarArchiveOutputStream(gzipOut)

  try {
    block(tarOut)
  } finally {
    tarOut.finish()
    tarOut.close()
    gzipOut.close()
    bufferOut.close()
    fileOut.close()
  }
}

类似:

withTarStream(filePath, s"$resultFile.tar.gz") { tarStream =>
  addFileToTarGz(tarStream, filePath + "/" + resultFolder, "")
}

【讨论】:

    【解决方案2】:

    根据@Mateusz Kubuszok 的建议,我尝试了以下变体:

    private def withResource[T: Resource : Manifest, X](t: T, block: T => X): X = managed(t).acquireAndGet(x => block(x))
    
    withResource(new FileOutputStream(file),
      (x:FileOutputStream) => withResource(new BufferedOutputStream(x),
        (y: BufferedOutputStream) => withResource(new GzipCompressorOutputStream(y),
          (z: GzipCompressorOutputStream) => withResource(new TarArchiveOutputStream(z),
            (tar: TarArchiveOutputStream) => writeTar(tar, filePath, resultFolder)))));
    

    然后还将上面的内容重构为以下形式:

    private def withResource[T: Resource : Manifest, X](t: T, block: T => X): X = managed(t).acquireAndGet(x => block(x))
    
    def writeInFile(x: FileOutputStream): Try[Unit] = withResource(new BufferedOutputStream(x), writeInBuffer)    
    def writeInBuffer(y: BufferedOutputStream): Try[Unit] = withResource(new GzipCompressorOutputStream(y), writeGzipStream)    
    def writeGzipStream(z: GzipCompressorOutputStream): Try[Unit] = withResource(new TarArchiveOutputStream(z), writeTarStream)    
    val file = new File(filePath, s"$resultFile.tar.gz")
    withResource(new FileOutputStream(file), writeInFile);
    

    第二天,一位同事提到了这个,看起来比上面两个都好:我还在探索如何将结果/错误传播到这个块之外。

    val file = new File(filePath, s"$resultFile.tar.gz")    
    for {   
      stream <- managed(new FileOutputStream(file)) 
      buffer <- managed(new BufferedOutputStream(stream))   
      gzip <- managed(new GzipCompressorOutputStream(buffer))   
      tar <- managed(new TarArchiveOutputStream(gzip))  
    } {
      writeTar(tar)
    }
    

    类似于@cchantep 的建议,我最终这样做了:

      val tarOutputStream: ManagedResource[TarArchiveOutputStream] = (for {
        stream <- managed(new FileOutputStream(file))
        buffer <- managed(new BufferedOutputStream(stream))
        gzip <- managed(new GzipCompressorOutputStream(buffer))
        tar <- managed(new TarArchiveOutputStream(gzip))
      } yield tar)
    
      Try (tarOutputStream.acquireAndGet(writeTarStream(_))) match {
        case Failure(e) => Failure(e)
        case Success(_) => Success(new File(s"$filePath/$runLabel.tar.gz"))
      }
    

    【讨论】:

      猜你喜欢
      • 2014-01-12
      • 1970-01-01
      • 1970-01-01
      • 2013-05-12
      • 2017-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-02
      相关资源
      最近更新 更多