I've built a JAR file for my Javalin application, and the code runs just fine. However, reading some resource files from the JAR fails with inputStream.available() == 0
, but it works just right for some other files.
The following files should be delivered just right:
a/
|\
| +- a.txt
| +- b.js
| +- c.css
|
b/
\
+- d.png
+- e.txt
However, the InputStream
only reads the files a/a.txt
, a/b.js
and b/e.txt
. For all other files it returns nothing, and available() == 0
, but it works when I'm not reading the files out of a JAR but from the extracted Classpath (and I'm using a ClassLoader
no matter what the execution environment looks like). Also, the file size does not matter, a/a.txt
is way larger than a/c.css
, so I'm out of clues on this end.
Some example code (as I said, I'm using Javalin for the HTTP request/response, which is being handled in the ctx
object, and I am also using Apache Tika to detect the MIME-Type of the files requested, which works as expected):
// Example, real path is (correctly) fetched from the Context (ctx) object
String path = "a/c.css";
ClassLoader classLoader = Thread.currentThread ().getContextClassLoader ();
InputStream inputStream = classLoader.getResourceAsStream (path);
String contentType = tika.detect (inputStream, path);
ctx.header ("Content-Type", contentType);
if (contentType.contains ("text") || contentType.contains ("script")) {
InputStreamReader streamReader = new InputStreamReader (inputStream);
BufferedReader reader = new BufferedReader (streamReader);
String line;
StringBuilder builder = new StringBuilder ();
while ((line = reader.readLine ()) != null) {
builder.append (line).append ("\n");
}
String result = builder.toString ();
ctx.header ("Content-Length", String.valueOf (result.length ()));
ctx.result (result);
reader.close ();
} else {
ctx.header ("Content-Length", String.valueOf (inputStream.available ()));
ctx.result (inputStream);
}
Am I missing something here or am I doing something wrong?
I suspect the MIME type detection might be the cause. First, try leaving it out and see whether that fixes your issue.
Here's my suspicion: tika.detect()
obviously needs to consume bytes from the given stream to be able to detect anything. The method javadocs state that it can only reset the stream if it supports marking. Thus, you should verify that the markSupported()
method of the InputStream
returned by classLoader.getResourceAsStream()
actually returns true
.
If it doesn't, Tika might e.g. consume all of the content of a file until it can be sure about the file type, then be unable to reset the stream, and then there's nothing left for you to send to the client. In that case, you should wrap the original InputStream
in a BufferedInputStream
(which adds the mark/reset feature) and use that for tika and for sending the content.
By the way: you should really use try with resources to make sure all input streams, stream reader and buffered readers are closed even when an exception is thrown.