How can I output a random image when in a jar file? - java

The below code works when running from my editor but the image fails to load when compiled into a runnable jar file with eclipse.
public static BufferedImage getRandomImage() {
// returns a random image from the Images folder
Random rand = new Random();
URL res = Card.class.getResource("Images"); // located in /src/.../Images
File f = new File(res.getFile());
if (!f.exists()) {
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
}
File[] files = f.listFiles();
int random = rand.nextInt(files.length);
BufferedImage img = null;
try {
img = ImageIO.read(files[random]);
} catch (IOException e) {
e.printStackTrace();
}
return img;
}
Could someone please suggest how I can modify my code or editor to load the files when compiled.
I have read other methods of accessing files but since I need to select randomly from a folder, I need to use the File class.

There is no safe way to list resources at runtime.
(Some people may suggest approaches which work sometimes, but will not work all the time. Class.getResource is not guaranteed to provide a listing; ProtectionDomain.getCodeSource can return null.)
But you don’t need to. It’s your application; you already know what files you put into it.
The best way is to either hard-code the list of files, or include a simple text file that contains a list of the files.
As an example, assume you created (or generated) a file named image-files.txt in which each line contains the base name of an image file, and embedded that file in your application:
List<String> imageNames;
try (BufferedReader linesReader = new BufferedReader(
new InputStreamReader(
Card.class.getResourceAsStream("image-files.txt"),
StandardCharsets.UTF_8));
Stream<String> lines = linesReader.lines()) {
imageNames = lines.collect(Collectors.toList());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
int random = rand.nextInt(imageNames.length());
String imageName = imageNames.get(random)));
BufferedImage img;
try {
img = ImageIO.read(Card.class.getResource(imageName));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return img;
Note: The getFile() method of URL does not return a valid filename. It only returns the path portion of a URL. There are many characters which would be illegal in URLs, so the path portion percent-escapes them. If you ignore this fact, the value returned by getFile() will eventually fail.
(The reason for the misleading method name is that the URL class was part of Java 1.0, and in the mid-1990s, all URLs actually referred to physical files.)
I need to use the File class
Each .jar entry is just a subsequence of compressed bytes within a single .jar file, so you will never be able to use File to read such an entry. Class.getResource and Class.getResourceAsStream are the only correct ways to read those entries.

The problem is that you are trying to access a URL of a resource as a file.
with this you can get all the images, and then you can do this:
List<String> arr = getResourceFiles("Images");
String imgPath = arr.get(rand.nextInt(arr.size()));
InputStream stream = Card.class.getResourceAsStream("Images/" + imgPath);
try {
img = ImageIO.read(stream);
} catch (IOException e) {
e.printStackTrace();
}
return img;

Related

Get resource file from jar file

Launching the jar, the console says that the file is not found and the font is not loaded.
How could I solve this problem?
I got this code:
public class FontLoader {
public static Font load(){
String fontFilePath = Paths.get(System.getProperty("user.dir"), "prova.jar", "Retro Gaming.ttf").toString();
int fontStyle = Font.BOLD;
int fontSize = CenterOnDefaultScreen.center().height*2/100;
Font font = null;
int fontTypeResource = Font.TRUETYPE_FONT;
if((fontFilePath == null || fontFilePath.isEmpty()) || fontSize < 1) {
throw new IllegalArgumentException("load() Method Error! Arguments " +
"passed to this method must contain a file path or a numerical " +
"value other than 0!" + System.lineSeparator());
}
try {
font = Font.createFont(fontTypeResource, new FileInputStream(
new File(fontFilePath))).deriveFont(fontStyle, fontSize);
}
catch (FileNotFoundException ex) {
System.out.println("FileNotFoundException: " + fontFilePath);
}
catch (FontFormatException | IOException ex) {
System.out.println("Exception thrown");
}
return font;
}
}
String fontFilePath = Paths.get(System.getProperty("user.dir"), "prova.jar", "Retro Gaming.ttf").toString();
That.. rather obviously won't work.
You need to use the gRAS (getResourceAsStream) system. File in java (as in, what new FileInputStream needs, the java.io.File object) are actual files. entries inside jar files don't count. It is not possible to refer to that ttf file with a File object, nor to open it with FileInputStream.
Fortunately, the createFont method doesn't demand that you pass a FileInputStream; any old InputStream will do.
The ttf file needs to be in the same classpath root as the this very class you are writing (for example, the same jar). Once you've ensured that is the case, you can use gRAS:
try (var fontIn = FontLoader.class.getResourceAsStream("/Retro Gaming.ttf")) {
Font.createFont(Font.TRUETYPE_FONT, fontIn).deriveFont(.., ...);
}
gRAS looks in the same place as where FontLoader.class lives. From your snippet it sounds like you put the ttf in the 'root' of the jar and not next to FontLoader. The leading slash in the string argument to getResourceAsStream means: Look relative to the root of the place FontLoader is in (so, your jar, presumably).

How to locate a file from user provided input in Java

I've got a program which reads in multiple json files and then does some analysis of the information contained in those files.
My project structure is laid out like :
/main
/java
/resources
/data
file1.json
file2.json
...
fileN.json
I'm attempting to give the user the ability to specify an alternate location if they have a different data set they want to analyze.
I'm using the following code to create an array of File objects:
ClassLoader loader = myClass.class.getClassLoader();
URL url = loader.getResource(location);
try {
String path = url.getPath();
return new File(path).listFiles();
} catch (NullPointerException e) {
throw new FileNotFoundException();
}
Note: I'm using myClass.class.getClassLoader() because I'm calling from a static method rather than from an instantiated object.
This works successfully when the location = "data". However if I pass in an absolute path to a different location (ex: location = "/Users/myuser/Desktop/data") that has the same data files in it I'm getting a NPE.
Is there a good way that will allow me to use the src/main/resources directory by default but allow my user to specify an absolute path to the data if they choose?
ClassLoader loader = myClass.class.getClassLoader();
URL url = loader.getResource(location);
The code above only works with files that exist in your class path.
Thus you can change it to read from src/main/resources directory by default and the absolute path given by the user by updating it to this:
try {
return new File(location).listFiles();
} catch (NullPointerException e) {
throw new FileNotFoundException();
}
It's very simple:
ClassLoader cl = myClass.class.getClassLoader();
URL url = cl.getResource(location);
if (url == null) {
//the location does not exist in class path
return new File(location).listFiles();
} else {
return new File(url.getPath()).listFiles();
}
But I think that the better way is:
private File[] readFile(String userLocation) {
if(userLocation == null || userLocation.isEmpty()) {
// user do not specify the path
return new File(myClass.class.getResource("data").getPath()).listFiles();
} else {
return new File(userLocation).listFiles();
}
}

How to copy files out of the currently running jar

I have a .jar that has two .dll files that it is dependent on. I would like to know if there is any way for me to copy these files from within the .jar to a users temp folder at runtime. here is the current code that I have (edited to just one .dll load to reduce question size):
public String tempDir = System.getProperty("java.io.tmpdir");
public String workingDir = dllInstall.class.getProtectionDomain().getCodeSource().getLocation().getPath();
public boolean installDLL() throws UnsupportedEncodingException {
try {
String decodedPath = URLDecoder.decode(workingDir, "UTF-8");
InputStream fileInStream = null;
OutputStream fileOutStream = null;
File fileIn = new File(decodedPath + "\\loadAtRuntime.dll");
File fileOut = new File(tempDir + "loadAtRuntime.dll");
fileInStream = new FileInputStream(fileIn);
fileOutStream = new FileOutputStream(fileOut);
byte[] bufferJNI = new byte[8192000013370000];
int lengthFileIn;
while ((lengthFileIn = fileInStream.read(bufferJNI)) > 0) {
fileOutStream.write(bufferJNI, 0, lengthFileIn);
}
//close all steams
} catch (IOException e) {
e.printStackTrace();
return false;
} catch (UnsupportedEncodingException e) {
System.out.println(e);
return false;
}
My main problem is getting the .dll files out of the jar at runtime. Any way to retrieve the path from within the .jar would be helpful.
Thanks in advance.
Since your dlls are bundeled inside your jar file you could just try to acasses them as resources using ClassLoader#getResourceAsStream and write them as binary files any where you want on the hard drive.
Here is some sample code:
InputStream ddlStream = <SomeClassInsideTheSameJar>.class
.getClassLoader().getResourceAsStream("some/pack/age/somelib.dll");
try (FileOutputStream fos = new FileOutputStream("somelib.dll");){
byte[] buf = new byte[2048];
int r;
while(-1 != (r = ddlStream.read(buf))) {
fos.write(buf, 0, r);
}
}
The code above will extract the dll located in the package some.pack.age to the current working directory.
Use a class loader that is able to locate resources in this JAR file. Either you can use the class loader of a class as Peter Lawrey suggested, or you can also create a URLClassLoader with the URL to that JAR.
Once you have that class loader you can retrieve a byte input stream with ClassLoader.getResourceAsStream. On the other hand you just create a FileOutputStream for the file you want to create.
The last step then is to copy all bytes from the input stream to the output stream, as you already did in your code example.
Use myClass.getClassLoader().getResourceAsStream("loadAtRuntime.dll"); and you will be able to find and copy DLLs in the JAR. You should pick a class which will also be in the same JAR.

How to get a path to a resource in a Java JAR file

I am trying to get a path to a Resource but I have had no luck.
This works (both in IDE and with the JAR) but this way I can't get a path to a file, only the file contents:
ClassLoader classLoader = getClass().getClassLoader();
PrintInputStream(classLoader.getResourceAsStream("config/netclient.p"));
If I do this:
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("config/netclient.p").getFile());
The result is:
java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)
Is there a way to get a path to a resource file?
This is deliberate. The contents of the "file" may not be available as a file. Remember you are dealing with classes and resources that may be part of a JAR file or other kind of resource. The classloader does not have to provide a file handle to the resource, for example the jar file may not have been expanded into individual files in the file system.
Anything you can do by getting a java.io.File could be done by copying the stream out into a temporary file and doing the same, if a java.io.File is absolutely necessary.
When loading a resource make sure you notice the difference between:
getClass().getClassLoader().getResource("com/myorg/foo.jpg") //relative path
and
getClass().getResource("/com/myorg/foo.jpg")); //note the slash at the beginning
I guess, this confusion is causing most of problems when loading a resource.
Also, when you're loading an image it's easier to use getResourceAsStream():
BufferedImage image = ImageIO.read(getClass().getResourceAsStream("/com/myorg/foo.jpg"));
When you really have to load a (non-image) file from a JAR archive, you might try this:
File file = null;
String resource = "/com/myorg/foo.xml";
URL res = getClass().getResource(resource);
if (res.getProtocol().equals("jar")) {
try {
InputStream input = getClass().getResourceAsStream(resource);
file = File.createTempFile("tempfile", ".tmp");
OutputStream out = new FileOutputStream(file);
int read;
byte[] bytes = new byte[1024];
while ((read = input.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.close();
file.deleteOnExit();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
} else {
//this will probably work in your IDE, but not from a JAR
file = new File(res.getFile());
}
if (file != null && !file.exists()) {
throw new RuntimeException("Error: File " + file + " not found!");
}
The one line answer is -
String path = this.getClass().getClassLoader().getResource(<resourceFileName>).toExternalForm()
Basically getResource method gives the URL. From this URL you can extract the path by calling toExternalForm().
I spent a while messing around with this problem, because no solution I found actually worked, strangely enough! The working directory is frequently not the directory of the JAR, especially if a JAR (or any program, for that matter) is run from the Start Menu under Windows. So here is what I did, and it works for .class files run from outside a JAR just as well as it works for a JAR. (I only tested it under Windows 7.)
try {
//Attempt to get the path of the actual JAR file, because the working directory is frequently not where the file is.
//Example: file:/D:/all/Java/TitanWaterworks/TitanWaterworks-en.jar!/TitanWaterworks.class
//Another example: /D:/all/Java/TitanWaterworks/TitanWaterworks.class
PROGRAM_DIRECTORY = getClass().getClassLoader().getResource("TitanWaterworks.class").getPath(); // Gets the path of the class or jar.
//Find the last ! and cut it off at that location. If this isn't being run from a jar, there is no !, so it'll cause an exception, which is fine.
try {
PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('!'));
} catch (Exception e) { }
//Find the last / and cut it off at that location.
PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('/') + 1);
//If it starts with /, cut it off.
if (PROGRAM_DIRECTORY.startsWith("/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(1, PROGRAM_DIRECTORY.length());
//If it starts with file:/, cut that off, too.
if (PROGRAM_DIRECTORY.startsWith("file:/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(6, PROGRAM_DIRECTORY.length());
} catch (Exception e) {
PROGRAM_DIRECTORY = ""; //Current working directory instead.
}
This is same code from user Tombart with stream flush and close to avoid incomplete temporary file content copy from jar resource and to have unique temp file names.
File file = null;
String resource = "/view/Trial_main.html" ;
URL res = getClass().getResource(resource);
if (res.toString().startsWith("jar:")) {
try {
InputStream input = getClass().getResourceAsStream(resource);
file = File.createTempFile(new Date().getTime()+"", ".html");
OutputStream out = new FileOutputStream(file);
int read;
byte[] bytes = new byte[1024];
while ((read = input.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.flush();
out.close();
input.close();
file.deleteOnExit();
} catch (IOException ex) {
ex.printStackTrace();
}
} else {
//this will probably work in your IDE, but not from a JAR
file = new File(res.getFile());
}
if netclient.p is inside a JAR file, it won't have a path because that file is located inside other file. in that case, the best path you can have is really file:/path/to/jarfile/bot.jar!/config/netclient.p.
You need to understand the path within the jar file.
Simply refer to it relative. So if you have a file (myfile.txt), located in foo.jar under the \src\main\resources directory (maven style). You would refer to it like:
src/main/resources/myfile.txt
If you dump your jar using jar -tvf myjar.jar you will see the output and the relative path within the jar file, and use the FORWARD SLASHES.
In my case, I have used a URL object instead Path.
File
File file = new File("my_path");
URL url = file.toURI().toURL();
Resource in classpath using classloader
URL url = MyClass.class.getClassLoader().getResource("resource_name")
When I need to read the content, I can use the following code:
InputStream stream = url.openStream();
And you can access the content using an InputStream.
follow code!
/src/main/resources/file
streamToFile(getClass().getClassLoader().getResourceAsStream("file"))
public static File streamToFile(InputStream in) {
if (in == null) {
return null;
}
try {
File f = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
f.deleteOnExit();
FileOutputStream out = new FileOutputStream(f);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
return f;
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
return null;
}
}
A File is an abstraction for a file in a filesystem, and the filesystems don't know anything about what are the contents of a JAR.
Try with an URI, I think there's a jar:// protocol that might be useful for your purpouses.
The following path worked for me: classpath:/path/to/resource/in/jar
private static final String FILE_LOCATION = "com/input/file/somefile.txt";
//Method Body
InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);
Getting this from getSystemResourceAsStream is the best option. By getting the inputstream rather than the file or the URL, works in a JAR file and as stand alone.
It may be a little late but you may use my library KResourceLoader to get a resource from your jar:
File resource = getResource("file.txt")
Inside your ressources folder (java/main/resources) of your jar add your file (we assume that you have added an xml file named imports.xml), after that you inject ResourceLoader if you use spring like bellow
#Autowired
private ResourceLoader resourceLoader;
inside tour function write the bellow code in order to load file:
Resource resource = resourceLoader.getResource("classpath:imports.xml");
try{
File file;
file = resource.getFile();//will load the file
...
}catch(IOException e){e.printStackTrace();}
Maybe this method can be used for quick solution.
public class TestUtility
{
public static File getInternalResource(String relativePath)
{
File resourceFile = null;
URL location = TestUtility.class.getProtectionDomain().getCodeSource().getLocation();
String codeLocation = location.toString();
try{
if (codeLocation.endsWith(".jar"){
//Call from jar
Path path = Paths.get(location.toURI()).resolve("../classes/" + relativePath).normalize();
resourceFile = path.toFile();
}else{
//Call from IDE
resourceFile = new File(TestUtility.class.getClassLoader().getResource(relativePath).getPath());
}
}catch(URISyntaxException ex){
ex.printStackTrace();
}
return resourceFile;
}
}
When in a jar file, the resource is located absolutely in the package hierarchy (not file system hierarchy). So if you have class com.example.Sweet loading a resource named ./default.conf then the resource's name is specified as /com/example/default.conf.
But if it's in a jar then it's not a File ...
This Class function can get absolute file path by relative file path in .jar file.
public class Utility {
public static void main(String[] args) throws Exception {
Utility utility = new Utility();
String absolutePath = utility.getAbsolutePath("./absolute/path/to/file");
}
public String getAbsolutePath(String relativeFilePath) throws IOException {
URL url = this.getClass().getResource(relativeFilePath);
return url.getPath();
}
}
If target file is same directory with Utility.java, your input will be ./file.txt.
When you input only / on getAbsolutePath(), it returns /Users/user/PROJECT_NAME/target/classes/. This means you can select the file like this /com/example/file.txt.

Creating a file is causing problem, the File.getPath() doesn't seem to work

I am trying to create a back up file for an html file on a web server.
I want the backup to be in the same location as the existing file (it's a quick fix). I want to create the file using File file = new File(PathName);
public void backUpOldPage(String oldContent) throws IOException{
// this.uri is a class variable with the path of the file to be backed up
String fileName = new File(this.uri).getName();
String pathName = new File(this.uri).getPath();
System.out.println(pathName);
String bckPath = pathName+"\\"+bckName;
FileOutputStream fout;
try
{
// Open an output stream
fout = new FileOutputStream (bckFile);
fout.close();
}
// Catches any error conditions
catch (IOException e)
{
System.err.println ("Unable to write to file");
System.exit(-1);
}
}
But if instead I was to set bckPath like this, it will work.
String bckPath = "C://dev/server/tomcat6/webapps/sample-site/index_sdjf---sd.html";
I am working on Windows, not sure if that makes a difference.
The result of String bckPath = pathName+"\"+bckName;
is bckPath = C:\dev\server\tomcat6\webapps\sample-site\filename.html - this doesn't result in a new file.
Use File.pathSeparator, that way you dont need to worry what OS you are using.
Try to use File.getCanonicalPath() instead of plain getPath(). This helps if the orginal path is not fully specified.
Regarding slashes, / or \ or File.pathSeparator is not causing the problem, because they are all the same on Windows and Java. (And you do not define bckFile in your code, only bckPath. Also use getCanonicalPath() on the new created bckPath.)

Categories

Resources