在scala中,当尝试用其他流包装它时,是否有一种惯用的方法来处理关闭和重新打开底层流?

时间:2017-09-06 15:06:24

标签: scala try-catch inputstream

我正在尝试打开一个流,尝试将其解压缩为gzip,如果失败则尝试将其解压缩为zlib,并返回该流以供进一步使用。在创建包装解压缩流的异常情况下,必须关闭基础流 ,否则我将遇到资源耗尽问题。

pbData是标准的,不可重置的InputStream

必须有一种更清洁的方法来做到这一点。

{{1}}

4 个答案:

答案 0 :(得分:1)

您的流程实际上是对InputStream个实例生成器的迭代。检查一下,与许多嵌套的try-catch' es相比,这是更惯用的解决方案:

val bis = new BufferedInputStream(pbData.open())
// allows us to read and reset 16 bytes, as in your answer
bis.mark(16)

// list of functions, because we need lazy evaluation of streams
val streamGens: List[Any => InputStream] = List(
  _ => new GZIPInputStream(bis),
  _ => new InflaterInputStream(bis),
  _ => bis
)

def firstStreamOf(streamGens: List[Any => InputStream]): Try[InputStream] =
  streamGens match {
    case x :: xs =>
      Try(x()).recoverWith {
        case NonFatal(_) =>
          // reset in case of failure
          bis.reset()
          firstStreamOf(xs)
      }
    case Nil =>
      // shouldn't get to this line because of last streamGens element
      Failure(new Exception)
  }

val tryStream = firstStreamOf(streamGens)

tryStream.foreach { stream =>
  // do something with stream...
}

作为奖励,如果您需要添加尝试更多流生成器,则必须在streamGens初始化中添加一行。此外,我们不需要手动添加bit.reset()调用。

答案 1 :(得分:0)

这样的事情,也许是:

def tryDecompress(pbData: PBData, ds: InputStream => InputStream *): InputStream = {
  def tryIt(s: InputStream, dc: InputStream => InputStream) = try {
    dc(s) 
  } catch { case NonFatal(e) => 
    close(s)
    throw e
  }

  val (first, rest) = ds.head -> ds.tail
  try { 
    tryIt(pbData.open, first) 
  } catch { 
    case _: ZipException if rest.nonEmpty => 
      tryDecompress(pbData, rest)
  }
}

val stream = tryDecompress(pbData, new GZipInputStream(_), new InflaterInputStream(_))

(太糟糕了scala的Try没有onFailure ...如果这样做可能看起来更好:/)

答案 2 :(得分:0)

val result: Try[InflaterInputStream] = Try(new GZIPInputStream(pb)) match {
    case res@Success(x) => res
    case Failure(e) => e match {
      case e: ZipException =>
        Try(new InflaterInputStream(pb)) match {
          case res2@Success(x2) => res2
          case Failure(e2) => e match {
            case _ => pb.close()
                      Failure(e2)
          }
        }
      case  ex@_ => pb.close()
                    Failure(ex)
    }
  }

答案 3 :(得分:0)

我发现使用BufferedInputStream来包装底层流,然后在每次解压缩库尝试之间重置它,看起来很干净。

val bis = new BufferedInputStream(pbData.open())
// allows us to read and reset 16 bytes
bis.mark(16)
val input: InputStream = {
  try {
    log.trace("attempting to open as gzip")
    new GZIPInputStream(bis)
  } catch {
    case e: ZipException => try {
        bis.reset()
        log.trace("attempting to open as zlib")
        new InflaterInputStream(bis)
      } catch {
        case e: ZipException => {
            bis.reset()
            log.trace("attempting to open as uncompressed")
            bis
          }
      }
  }
}