我正在尝试打开一个流,尝试将其解压缩为gzip,如果失败则尝试将其解压缩为zlib,并返回该流以供进一步使用。在创建包装解压缩流的异常情况下,必须关闭基础流 ,否则我将遇到资源耗尽问题。
pbData是标准的,不可重置的InputStream
必须有一种更清洁的方法来做到这一点。
{{1}}
答案 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
}
}
}
}