I have this code which reads all the files from a directory.
File textFolder = new File(\"text_directory\");
File [] texFiles = textFolder.listFiles
So I guess my main problem would be, how to know the name of the jar where my main class lives.
Assuming that your project is packed in a Jar (not necessarily true!), you can use ClassLoader.getResource() or findResource() with the class name (followed by .class) to get the jar that contains a given class. You'll have to parse the jar name from the URL that gets returned (not that tough), which I will leave as an exercise for the reader :-)
Be sure to test for the case where the class is not part of a jar.
The most robust mechanism for listing all resources in the classpath is currently to use this pattern with ClassGraph, because it handles the widest possible array of classpath specification mechanisms, including the new JPMS module system. (I am the author of ClassGraph.)
How to know the name of the JAR file where my main class lives?
URI mainClasspathElementURI;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
.enableClassInfo().scan()) {
mainClasspathElementURI =
scanResult.getClassInfo("x.y.z.MainClass").getClasspathElementURI();
}
How can I read the contents of a directory in a similar fashion within a JAR file?
List<String> classpathElementResourcePaths;
try (ScanResult scanResult = new ClassGraph().overrideClasspath(mainClasspathElementURI)
.scan()) {
classpathElementResourcePaths = scanResult.getAllResources().getPaths();
}
There are lots of other ways to deal with resources too.
public static ArrayList<String> listItems(String path) throws Exception{
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(path);
byte[] b = new byte[in.available()];
in.read(b);
String data = new String(b);
String[] s = data.split("\n");
List<String> a = Arrays.asList(s);
ArrayList<String> m = new ArrayList<>(a);
return m;
}
One more for the road:
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;
import static java.nio.file.FileSystems.newFileSystem;
import static java.util.Collections.emptyMap;
public class ResourceWalker {
private static final PathMatcher FILE_MATCHER =
FileSystems.getDefault().getPathMatcher( "glob:**.ttf" );
public static List<Path> walk( final String directory )
throws URISyntaxException, IOException {
final List<Path> filenames = new ArrayList<>();
final var resource = ResourceWalker.class.getResource( directory );
if( resource != null ) {
final var uri = resource.toURI();
final var path = uri.getScheme().equals( "jar" )
? newFileSystem( uri, emptyMap() ).getPath( directory )
: Paths.get( uri );
final var walk = Files.walk( path, 10 );
for( final var it = walk.iterator(); it.hasNext(); ) {
final Path p = it.next();
if( FILE_MATCHER.matches( p ) ) {
filenames.add( p );
}
}
}
return filenames;
}
}
This is a bit more flexible for matching specific filenames because it uses wildcard globbing.
A more functional style:
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.util.function.Consumer;
import static java.nio.file.FileSystems.newFileSystem;
import static java.util.Collections.emptyMap;
/**
* Responsible for finding file resources.
*/
public class ResourceWalker {
private static final PathMatcher FILE_MATCHER =
FileSystems.getDefault().getPathMatcher( "glob:**.ttf" );
public static void walk( final String dirName, final Consumer<Path> f )
throws URISyntaxException, IOException {
final var resource = ResourceWalker.class.getResource( dirName );
if( resource != null ) {
final var uri = resource.toURI();
final var path = uri.getScheme().equals( "jar" )
? newFileSystem( uri, emptyMap() ).getPath( dirName )
: Paths.get( uri );
final var walk = Files.walk( path, 10 );
for( final var it = walk.iterator(); it.hasNext(); ) {
final Path p = it.next();
if( FILE_MATCHER.matches( p ) ) {
f.accept( p );
}
}
}
}
}
erickson's answer worked perfectly:
Here's the working code.
CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
List<String> list = new ArrayList<String>();
if( src != null ) {
URL jar = src.getLocation();
ZipInputStream zip = new ZipInputStream( jar.openStream());
ZipEntry ze = null;
while( ( ze = zip.getNextEntry() ) != null ) {
String entryName = ze.getName();
if( entryName.startsWith("images") && entryName.endsWith(".png") ) {
list.add( entryName );
}
}
}
webimages = list.toArray( new String[ list.size() ] );
And I have just modify my load method from this:
File[] webimages = ...
BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex].getName() ));
To this:
String [] webimages = ...
BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex]));
Just a different way of listing/reading files from a jar URL and it does it recursively for nested jars
https://gist.github.com/trung/2cd90faab7f75b3bcbaa
URL urlResource = Thead.currentThread().getContextClassLoader().getResource("foo");
JarReader.read(urlResource, new InputStreamCallback() {
@Override
public void onFile(String name, InputStream is) throws IOException {
// got file name and content stream
}
});