One commonly wants to use icons, configuration files, and other kinds of resources in Java programs. While there are many ways to do accomplish, doing it in a way that is portable requires some thought because of the way different IDEs and deployment mechanisms (e.g., .jar files ) organize code.
To easiest way to refer to icons in a portable fashion is to use a URL
object (from the java.net
package), and the easiest way to get the appropriate URL
object is to use a Class
object. For example, if you want to get the URL
for a resource named logo.png
that is in the icons
directory under the top-level directory containing the class of the object referred to by this
then you would do the following:
URL url = this.getClass().getResource("/images/logo.png").
You could then load it as follows:
ImageIcon icon = new ImageIcon(url);
The static getBundle()
method in the ResourceBundle
class can be passed a ClassLoader
that it will use to locate the resources. For example, if you want to get the ResourceBundle
with the prefix Strings
that is in the top-level directory containing the class of the object referred to by this
then you would do the following:
ResourceBundle strings; strings = ResourceBundle.getBundle("Strings", Locale.getDefault(), this.getClass().getClassLoader());
For other kinds of resources (e.g., configuration files), the easiest way to ensure that your code is portable is to use an InputStream
object (from the java.io
package) to refer to the resource and use a Class
object to retrieve the appropriate InputStream
object.
For example, if you want to get the InputStream
for a text resource named default.cfg
that is in the configurations
directory under the top-level directory containing the class of the object referred to by this
then you would do the following:
InputStream is = this.getClass().getResourceAsStream("/configurations/config.txt").
You could then read the first line of this text resource as follows:
BufferedReader in = new BufferedReader(new InputStreamReader(is)); String line = in.readLine();
Sometimes it is necessary to copy resources from a .jar file to a temporary directory on the files system (e.g., if you want to load .html files that are in a .jar file into a browser). The following utility class can be used for this purpose.
import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.List; import java.util.stream.Stream; public class ResourceCopier { /** * Copy all of the files from a resources package to a temporary directory * on the file system (whether or not the code and resources are in a .jar * file or on the file system). * * This implementation assumes the resource package is a subpackage of the * package that contains this class (though that can be changed_. * * @param id The ID to use for the temporary directory * @param subdir The name of the subpackage that contains the resources * @return The Path of the temporary directory * @throws IOException If something goes wrong * @throws URISyntaxException If something goes wrong */ public static Path copyResourcesToTemp(String id, String subdir) throws IOException, URISyntaxException { // Get the location of the ResourceCopier.class file String canonicalName = ResourceCopier.class.getName(); String packageName = ResourceCopier.class.getPackageName(); String className = canonicalName.substring(packageName.length()+1) + ".class"; String thisLocation = ResourceCopier.class.getResource(className).toString(); // Remove the file name from the location and create a URI int fileStart = thisLocation.indexOf(className); String rootURL = thisLocation.substring(0, fileStart); URI sourceURI = new URI(rootURL + subdir); // Get the Path for source files (whether in a .jar file or the file system) Path sourcePath; FileSystem fileSystem = null; if (sourceURI.getScheme().equals("jar")) { fileSystem = FileSystems.newFileSystem(sourceURI, new HashMap<String, Object>()); sourcePath = fileSystem.getPath("/" + packageName + "/" + subdir); } else { sourcePath = Paths.get(sourceURI); } // Get a List of all of the files in the source Path Stream<Path> files = Files.list(sourcePath); List<Path> filesList = files.toList(); // Create a temporary directory on the local file system Path temp = Files.createTempDirectory(id); Path destinationPath = Path.of(temp.toString()); // Copy all of the files from the source Path to the temporary directory for (Path file : filesList) { Path targetFile = Path.of(destinationPath.toString(), file.getFileName().toString()); Files.copy(file, targetFile); } files.close(); if (fileSystem != null) fileSystem.close(); return destinationPath; } }
These technique use the ClassLoader
infrastructure to retrieve resources from the .jar
file. So, it is very important that you understand the relative locations of the resource and the object that you call getClass()
on. A leading /
will start at the root of the directory tree, but you still must understand where in the .jar
file is the root. Omitting the leading /
, on the other hand, will start at the location of the .class
file of the object referred to by this
.