1

I am often developing applications in an IDE and then distributing them with an executable JAR. So I seek interoperability in regards to library functions. I have a partial solution using an InputStream to robustly read data from a file located in either a FileSystem or a Jar file, but I would like to make use of the robust Kotlin function Closable.use() to adapt to the error being thrown or not.

After some struggle I found this post that explains how compression inside the Jar file makes its access different than that from a FileSystem. In particular the nice Kotlin extension function File.readBytes() will not work for a file in a Jar. That is a shame because looking into the source of readBytes() there is a clever use of the use() function to handle the stream contingencies. It says...

closes it down correctly whether an exception is thrown or not.

Here is the partial solution which returns a ByteArray. These functions work the same whether running the application from within the IDE or from an executable Jar.

inline fun <reified T> getResourceAsStream(filename:String):InputStream {
  //it is calling function's responsibility to close the stream
  //force the first character to be a backslash that indicates root
  val fnameWithPath= if (filename[0] != '/') "/$filename" else filename
  //extract the stream if it exists
  return T::class.java.getResourceAsStream(fnameWithPath)
}
inline fun <reified T> getResourceByteArray(filename:String):ByteArray {
  //will open a stream, read bytes and then close stream
  val stream= getResourceAsStream<T>(filename)
  val byteArray = stream.readBytes()
  stream.close()
  return byteArray
}
J.E.Tkaczyk
  • 557
  • 1
  • 8
  • 19
  • Hi, I've read through your question a few times but I fail to see what you are asking. What is your question? – Erwin Bolwidt Sep 13 '18 at 03:47
  • @ErwinBolwidt you are right, my question was not clear. I wanted to know the Kotlin way to properly close the `InputStream` and deal with the possibility of the file not existing or other errors thrown. As a lead, it seems that this same problem was solved in `File.readBytes()` using the Kotlin library function `use()`. Subsequently, I was successful integrating this `use()` function and posted my solution below. There may be other issues concerning reading robustly from both the FileSystem and a Jar that I'm missing. So I'm posting my best idea and looking for any improvements. – J.E.Tkaczyk Sep 13 '18 at 15:59

2 Answers2

2

You're almost there.  What you might have missed is that Kotlin already defines an extension method use() on the Closable interface, which InputStream implements.  So your second method can be rewritten as a one-liner:

inline fun <reified T> getResourceByteArray(filename: String)
    = getResourceAsStream<T>(filename).use{ it.readBytes() }
gidds
  • 16,558
  • 2
  • 19
  • 26
0

I incorporated @gidds ' solution to make use of the Kotlin library function Closeable.use() as part of the operation to load the ByteArray from a resource file through an InputStream. I'm expecting the use() function to take care of closing the InputStream or otherwise dealing with errors thrown or not.

In the updated code, the calling Class provided as a reified type gives the context for which resources directory will contain the file. In a multi-project build, there may be several resources directories. It is the case I'm operating under with different projects requiring different input data.

inline fun <reified T> getResourceAsStream(filename:String):InputStream {
  //it is calling function's responsibility to close the stream
  //force the first character to be a backslash that indicates root
  val fnameWithPath= if (filename[0] != '/') "/$filename" else filename
  //extract the stream if it exists
  return T::class.java.getResourceAsStream(fnameWithPath)
}

inline fun <reified T> getResourceByteArray(filename: String)
= getResourceAsStream<T>(filename).use{ it.readBytes() }

class ReadByteData() {
  init{
    val byteArray= getResourceByteArray<ReadByteData>("fileName")
  }
}
J.E.Tkaczyk
  • 557
  • 1
  • 8
  • 19