Different Ways of Loading a File as an Inputstream

Different ways of loading a file as an InputStream

There are subtle differences as to how the fileName you are passing is interpreted. Basically, you have 2 different methods: ClassLoader.getResourceAsStream() and Class.getResourceAsStream(). These two methods will locate the resource differently.

In Class.getResourceAsStream(path), the path is interpreted as a path local to the package of the class you are calling it from. For example calling, String.class.getResourceAsStream("myfile.txt") will look for a file in your classpath at the following location: "java/lang/myfile.txt". If your path starts with a /, then it will be considered an absolute path, and will start searching from the root of the classpath. So calling String.class.getResourceAsStream("/myfile.txt") will look at the following location in your class path ./myfile.txt.

ClassLoader.getResourceAsStream(path) will consider all paths to be absolute paths. So calling String.class.getClassLoader().getResourceAsStream("myfile.txt") and String.class.getClassLoader().getResourceAsStream("/myfile.txt") will both look for a file in your classpath at the following location: ./myfile.txt.

Everytime I mention a location in this post, it could be a location in your filesystem itself, or inside the corresponding jar file, depending on the Class and/or ClassLoader you are loading the resource from.

In your case, you are loading the class from an Application Server, so your should use Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName) instead of this.getClass().getClassLoader().getResourceAsStream(fileName). this.getClass().getResourceAsStream() will also work.

Read this article for more detailed information about that particular problem.



Warning for users of Tomcat 7 and below

One of the answers to this question states that my explanation seems to be incorrect for Tomcat 7. I've tried to look around to see why that would be the case.

So I've looked at the source code of Tomcat's WebAppClassLoader for several versions of Tomcat. The implementation of findResource(String name) (which is utimately responsible for producing the URL to the requested resource) is virtually identical in Tomcat 6 and Tomcat 7, but is different in Tomcat 8.

In versions 6 and 7, the implementation does not attempt to normalize the resource name. This means that in these versions, classLoader.getResourceAsStream("/resource.txt") may not produce the same result as classLoader.getResourceAsStream("resource.txt") event though it should (since that what the Javadoc specifies). [source code]

In version 8 though, the resource name is normalized to guarantee that the absolute version of the resource name is the one that is used. Therefore, in Tomcat 8, the two calls described above should always return the same result. [source code]

As a result, you have to be extra careful when using ClassLoader.getResourceAsStream() or Class.getResourceAsStream() on Tomcat versions earlier than 8. And you must also keep in mind that class.getResourceAsStream("/resource.txt") actually calls classLoader.getResourceAsStream("resource.txt") (the leading / is stripped).

Loading a file from Internal storage to InputStream same way as from Assets

You clearly cannot work with the InputStream at the point you labelled "Same work with the certificate" since you have already called close() on it by that point.

Move your call to close() to after you have finished with the stream.

How to load the file from the inputstream?

You can actually use: GroovyShell#parse(Reader) method:

Script result = new GroovyShell().parse(new InputStreamReader(in));

Load a file from external storage to Inputstream

Try

File file = new File(Uri.toString());
FileInputStream fileInputStream = new FileInputStream(file);

Then you can read from the stream.

Load a file in Resources with FileInputStream

I see 2 solutions:

Solution 1

Read the classpath ressource to a temp file and delete it after you called load(File)

InputStream cpResource = this.getClass().getClassLoader().getResourceAsStream("file.name");
File tmpFile = File.createTempFile("file", "temp");
FileUtils.copyInputStreamToFile(cpResource, tmpFile); // FileUtils from apache-io
try {
decider.load(tmpFile);
} finally {
tmpFile.delete();
}

Solution 2

If the ClassLoader that loads the resource is a URLClassLoader you can try to find the absolute file name. But this only works if the resource you want exists as a file on the filesystem. It doesn't work if the file is contained in a jar.

ClassLoader classLoader = this.getClass().getClassLoader();
if(classLoader instanceof URLClassLoader){
URLClassLoader urlClassLoader = URLClassLoader.class.cast(classLoader);
URL resourceUrl = urlClassLoader.findResource("file.name");

if("file".equals(resourceUrl.getProtocol())){
URI uri = resourceUrl.toURI();
File file = new File(uri);
decider.load(file);
}
}

I would suggest to write a utility class that tries to find the absolute file through the class loader or if it can't get it this way uses the temp file approach as fallback.

Or in a more object-oriented way:

    public class FileResourceTest {

public static void main(String[] args) throws IOException {
File resourceAsFile = getResourceAsFile("file.name");
System.out.println(resourceAsFile);
}

private static File getResourceAsFile(String resource) throws IOException {
ClassLoader cl = FileResourceTest.class.getClassLoader();
File file = null;
FileResource fileResource = new URLClassLoaderFileResource(cl, resource);
try {
file = fileResource.getFile();
} catch (IOException e) {
fileResource = new ClasspathResourceFileResource(cl, resource);
file = fileResource.getFile();
}
return file;
}

public static interface FileResource {

public File getFile() throws IOException;

}

public static class ClasspathResourceFileResource implements FileResource {

private ClassLoader cl;
private String resource;

public ClasspathResourceFileResource(ClassLoader cl, String resource) {
this.cl = cl;
this.resource = resource;
}

public File getFile() throws IOException {
InputStream cpResource = cl.getResourceAsStream(resource);
File tmpFile = File.createTempFile("file", "temp");
FileUtils.copyInputStreamToFile(cpResource, tmpFile);
tmpFile.deleteOnExit();
return tmpFile;
}

}

public static class URLClassLoaderFileResource implements FileResource {

private ClassLoader cl;
private String resource;

public URLClassLoaderFileResource(ClassLoader cl, String resourcePath) {
this.cl = cl;
this.resource = resourcePath;
}

public File getFile() throws IOException {
File resourceFile = null;
if (cl instanceof URLClassLoader) {
URLClassLoader urlClassLoader = URLClassLoader.class.cast(cl);
URL resourceUrl = urlClassLoader.findResource(resource);
if ("file".equals(resourceUrl.getProtocol())) {
try {

URI uri = resourceUrl.toURI();
resourceFile = new File(uri);
} catch (URISyntaxException e) {
IOException ioException = new IOException(
"Unable to get file through class loader: "
+ cl);
ioException.initCause(e);
throw ioException;
}

}
}
if (resourceFile == null) {
throw new IOException(
"Unable to get file through class loader: " + cl);
}
return resourceFile;
}

}
}

You can also use a thrid party library like commons-vfs that allows you to reference a file within a jar. E.g. jar:// arch-file-uri[! absolute-path]. Since commons-vfs specifies an own FileObject that represents a file you must still copy the content to a local java.io.File to adapt to the Decider.load(File) API.

EDIT

This is very helpful! Is there anything in newer versions of java that already supports this requirement?

Even if I haven't take a look at every class of every newer version I would say no. Because a classpath resource is not always a file. E.g. it can be a file within a jar or even a remote resource. Think about the applets that java programmers used a long time ago. Thus the concept of a classpath and it's resources is not bound to a local filsystem. This is obviously a good thing, because you can load classes from almost every URL and this makes it more flexible. But this flexibility also means that you must read the resource and create a copy if you need a File.

But maybe some kind of utility code like the one I showed above will make it into the JRE. Maybe it is already there. If so please comment and let us all know.

Inputstream is null, while loading a file from a file path in windows service

This is because of a classpath issue between resources and files. We can not use classloaders to access files. For that we need to use File, filereader, file input stream. After changing like this everything works fine;

InputStream in = new FileInputStream(filePath);

How do I load a file from resource folder?

Try the next:

ClassLoader classloader = Thread.currentThread().getContextClassLoader();
InputStream is = classloader.getResourceAsStream("test.csv");

If the above doesn't work, various projects have been added the following class: ClassLoaderUtil1 (code here).2

Here are some examples of how that class is used:

src\main\java\com\company\test\YourCallingClass.java
src\main\java\com\opensymphony\xwork2\util\ClassLoaderUtil.java
src\main\resources\test.csv
// java.net.URL
URL url = ClassLoaderUtil.getResource("test.csv", YourCallingClass.class);
Path path = Paths.get(url.toURI());
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
// java.io.InputStream
InputStream inputStream = ClassLoaderUtil.getResourceAsStream("test.csv", YourCallingClass.class);
InputStreamReader streamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader);
for (String line; (line = reader.readLine()) != null;) {
// Process line
}

Notes

  1. See it in The Wayback Machine.
  2. Also in GitHub.

Best practice reading file as InputStream

Once you have your RandomAccessFile open, you can do something like this:

RandomAccessFile raf; // Initialized elsewhere

int bufferSize = 1024*1024; // 1 MB
int offsetIntoFile = 0;
Uint8List byteBuffer = new Uint8List(bufferSize); // 1 MB
Future<int> bytesReadFuture = raf.readList(byteBuffer, offsetIntoFile, bufferSize);

bytesReadFuture.then((bytesRead) {
Do something with byteBuffer here.
});

There is also a synchronous call readListSync.

John



Related Topics



Leave a reply



Submit