Extract resource folder from running jar in Java 7 - java

My resources folder inside my jar includes a directory with several binary files. I am attempting to use this code to extract them:
try(InputStream is = ExternalHTMLThumbnail.class.getResourceAsStream("/wkhtmltoimage")) {
Files.copy(is, Paths.get("/home/dan/wkhtmltoimage");
}
This is throwing the error
java.nio.file.NoSuchFileException: /home/dan/wkhtmltoimage
Which comes from
if (errno() == UnixConstants.ENOENT)
return new NoSuchFileException(file, other, null);
in UnixException.java. Even though in Files.java the correct options are passed:
ostream = newOutputStream(target, StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE);
from Files.copy. Of course there's not! That's why I'm trying to make it. I don't yet understand Path and Files enough to do this right. What's the best way to extract the directory and all its contents?
Confused because the docs for Files.copy claims
By default, the copy fails if the target file already exists or is a symbolic link
(Apparently it fails if the target file doesn't exist as well?)
And lists the possible exceptions, and NoSuchFileException is not one of them.

If you're using Guava:
URL url = Resources.getResource(ExternalHTMLThumbnail.class, "wkhtmltoimage");
byte[] bytes = Resources.toByteArray(url);
Files.write(bytes, new File("/my/path/myFile"));
You could of course just chain that all into one line; I declared the variables to make it more readable.

The file that does not exist may actually be the directory you're trying to create the file in.
/home/dan/wkhtmltoimage
Does /home/dan exist? Probably not if you're on a Mac.

Related

Java can't access file on local path

Asked this question, having already tried possible solutions in other questions here on stack but that didn't allow me to fix the problem.
As in the title, I have created a java utility with which I have to perform operations on text files, in particular I have to perform simple operations to move between directories, copy from one directory to another, etc.
To do this I have used the java libraries java.io.File and java.nio.*, And I have implemented two functions for now,copyFile(sourcePath, targetPath) and moveFile(sourcePath, targetPath).
To develop this I am using a mac, and the files are under the source path /Users/myname/Documents/myfolder/F24/archive/, and my target path is /Users/myname/Documents/myfolder/F24/target/.
But when I run my code I get a java.nio.file.NoSuchFileException: /Users/myname/Documents/myfolder/F24/archive
Having tried the other solutions here on stack and java documentation already I haven't been able to fix this yet ... I accept any advice or suggestion
Thank you all
my code:
// copyFile: funzione chiamata per copiare file
public static boolean copyFile(String sourcePath, String targetPath){
boolean fileCopied = true;
try{
Files.copy(Paths.get(sourcePath), Paths.get(targetPath), StandardCopyOption.REPLACE_EXISTING);
}catch(Exception e){
String sp = Paths.get(sourcePath)+"/";
fileCopied = false;
System.out.println("Non posso copiare i file dalla cartella "+sp+" nella cartella "+Paths.get(targetPath)+" ! \n");
e.printStackTrace();
}
return fileCopied;
}
Files.copy cannot copy entire directories. The first 'path' you pass to Files.copy must ALL:
Exist.
Be readable by the process that runs the JVM. This is non-trivial on a mac, which denies pretty much all disk rights to all apps by default until you give it access. This can be tricky for java apps. I'm not quite sure how you fix it (I did something on my mac to get rid of that, but I can't remember what - possibly out of the box java apps just get to read whatever they want and it's only actual mac apps that get pseudo-sandboxed. Point is, there's a chance it's mac's app access control denying it even if the unix file rights on this thing indicate you ought to be able to read it).
Be a plain old file and not a directory or whatnot.
Files.move can (usually - depends on impl and underlying OS) usually be done to directories, but not Files.copy. You're in a programming language, not a shell. If you want to copy entire directories, write code that does this.
Not sure whether my comment is understood though answered.
Ìn java SE target must not be the target directory. In other APIs of file copying
one can say COPY FILE TO DIRECTORY. In java not so; this was intentionally designed to remove one error cause.
That style would be:
Path source = Paths.get(sourcePath);
if (Files.isRegularFile(source)) {
Path target = Paths.get(targetPath);
Files.createDirectories(target);
if (Files.isDirectory(target)) {
target = Paths.get(targetPath, source.getFileName().toString());
// Or: target = target.resolve(source.getFileName().toString());
}
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
}
Better ensure when calling to use the full path.

How to read a file that is located in any location on the computer?

So I have a project, and this is one of the demands:
You should have a class named Project3, containing a main method.
This program reads the levels information from a file whose name is
specified as a command-line parameter (The file should also be
relative to the class-path as described here:)
All the file names specified in the levels and block definition files
should be relative to the class path. The reason we want them to be
relative to the class path is that later we will be able to read the
files from inside a jar, something we can not do with regular File
references.
To get an input stream relative to the class path (even if it's inside
a jar), use the following:
InputStream is =
ClassLoader.getSystemClassLoader().getResourceAsStream("image.png");
The idea is to keep a folder with files(definitions and images) and
then add that folder to the class path when running the JVM:
java -cp bin:resources ... If you don't add the resources folder to
you class path you wont be able to load them with the command from
above.
When run without parameters, your program should read a default level
file, and run the game accordingly. The location of the default level
file should be hard-coded in your code, and be relative to the
classpath_.
When run without parameters, your program should read a default level file, and run the game accordingly. The location of the default level file should be hard-coded in your code, and be relative to the classpath_.
The part of the code that handles the input is:
public Void run() throws IOException {
LevelReader level = new LevelReader();
List<level> chosenLevels = new ArrayList<>();
if (args.length >= 1) {
File f = new File(args[0]);
if (f.exists()) {
chosenLevels = level.makeLevel(args[0]);
}
}
if (chosenLevels.size() == 0) {
game.runLevels(defaultLevels);
} else {
game.runLevels(chosenLevels);
}
return null;
}
So my question is:
An argument should be the full path of a file which means:
D:\desktop\level3.txt
Is it possible to read a file from every location on my computer?
Because right now I can do it only if my text file is in the
project's directory (not even in the src folder).
I can't understand the rest of their demands. What does is mean "should be hard-coded in your code, and be relative to the
classpath_." and why is it related to InputStream method(?)
I'm confused all over this.
Thanks.
A classpath resource is not the same as a file.
As you have correctly stated, the full path of a file is something like D:\desktop\level3.txt.
But if ever want to distribute your application so it can run on other computers, which probably won’t have that file in that location, you have two choices:
Ask the user to tell the program where to find the file on their computer.
Bundle the file with the compiled program.
If you place a non-.class file in the same place as .class files, it’s considered a resource. Since you don’t know at runtime where your program’s class files are located,¹ you use the getResource or getResourceAsStream method, which is specifically designed to look in the classpath.
The getResource* methods have the additional benefit that they will work both when you are developing, and when the program is packaged as a .jar file. Individual entries in a .jar file are not separate files and cannot be read using the File or FileInputStream classes.
If I understand your assignment correctly, the default level file should be an application resource, and the name of that resource is what should be hard-coded in your program. Something like:
InputStream is;
if (args.length > 0) {
is = new BufferedInputStream(
new FileInputStream(args[0]));
} else {
// No argument provided, so use program's default level data.
is = ClassLoader.getSystemClassLoader().getResourceAsStream("defaultlevel.txt");
}
chosenLevels = level.makeLevel(is);
¹ You may find some pages that claim you can determine the location of a running program’s code using getProtectionDomain().getCodeSource(), but getCodeSource() may return null, depending on the JVM and ClassLoader implementation, so this is not reliable.
To answer your first question, it doesn't seem like they're asking you to read from anywhere on disk, just from within your class path. So that seems fine.
The second question, "What does is mean 'should be hard-coded in your code, and be relative to the classpath'?". You are always going to have a default level file in your project directory. Define the path to this file as a String in your program and that requirement will be satisfied. It's related to the InputStream because the stream requires a location to read in from.

how to get file size in truzip [duplicate]

This question already has an answer here:
How can you get the size of a file in an archive using TrueZip?
(1 answer)
Closed 6 years ago.
I am using truezip version (7.7.9) to update a archive file.
The way I am doing it is as follows
File entry = new TFile(filepath);
writer = new TFileWriter(entry);
writer.append("MyString");
writer.flush();
long fileSize = entry.length(); // which always gives value as 0
I need the exact file size for some purpose
But this always gives 0
Is there any other way I can get that.
I read the documentation of TFile class\
https://truezip.java.net/apidocs/de/schlichtherle/truezip/file/TFile.html#length()
couldn't quite understand what it does
From the FAQ:
Every now and then you might want to treat an archive file like a
regular file rather than a virtual directory. For example, when trying
to obtain the length of the archive file in bytes. You would normally
do this by calling the method File.length(). However, if the File
object is an instance of the TFile class and the path has been
detected to name a valid archive file, then this method would always
return zero. This is because you might have changed the archive file
and then it would be impossible to return a precise result until the
changes have been committed to the target archive file.
It seems you can either compute the size of the file before changing it and then add the size of what you are appending, or you need to write the file and then call File.length(). You can do this by calling TVFS.unmount() or TVFS.unmount(TFile)
See this article at official page
The API should not detect an individual archive file as a virtual directory. How can I do this
Look at this
[...]
However, if the File object is an instance of the TFile class and the path has been detected to name a valid archive file, then this method would always return zero. This is because you might have changed the archive file and then it would be impossible to return a precise result until the changes have been committed to the target archive file..
It also applies on TPath
I wonder if you can use Files.size(path) in order to get what you need (after & before changes on your TZip)
Hope It will help.
If the archive file is valid, then it returns 0. If there is any problem occurs then it will give IOException.
If you change anything, then you need to call the method TFile.umount() to commit all changes.
Then use the following method to obtain a TFile which does not detect the archive file and call its length() method:
In TrueZIP 7.5, you can just call TFile.toNonArchiveFile()
// Note that the actual path may refer to anything, even a nested archive file.
TFile inner = new TFile("outer.zip/inner.zip");
TFile file = inner.toNonArchiveFile(); // convert - since TrueZIP 7.5
... // there may be some I/O here
TVFS.umount(inner); // unmount potential archive file
// Now you can safely do any I/O to $file.
long length = file.length();
You can do it by another way also:
private static TFile newNonArchiveFile(TFile file) {
return new TFile(file.getParentFile(), file.getName(), TArchiveDetector.NULL);
}
Resource Link:
How can you get the size of a file in an archive using TrueZip?

Loading files within android

Android seems to make life pretty easy for loading resources of certain types. Once I leave the beaten path a little bit, it seems less helpful. I can load in images, sounds and other objects from the assets folder if and only if they are of certain types. If I try to load a binary file of my own format, I get a less than helpful 'file not found' exception, even though listing the directory shows that it is clearly there.
I've tried using the following methods to read a binary file of a custom format from the assets directory:
File jfile = new File("file://android_asset/"+filename); //tried to get the URI of the assets folder
JarFile file = new JarFile("assets/"+filename); //tried assuming the assets folder is root
fd = am.openNonAssetFd( filename); //tried getting my file as an non asset in the assets folder (n.b. it is definitely there)
fs = am.open(filename, AssetManager.ACCESS_BUFFER); //tried loading it as an asset
I'm thinking that there's something fundamental about android file I/O that I don't understand yet. The documentation for asset management seems incomplete and there must be some reason for deliberately making this unintuitive (something to do with security?). So, what's the fool proof, canonical way of loading a binary file of my own format within an android app?
UPDATE:
I tried file:///android_asset/ but still no joy.
String fullfilename = "file:///android_asset/"+filename;
File jfile = new File(fullfilename);
if (jfile.exists())
{
return new FileInputStream(jfile);
}
else
{
return null; //the file does exist but it always says it doesn't.
}
Are there any permissions for the file or in the project manifest that I need?
Thanks
I think the best way to load a file from the Assets folder would be to use AssetManager.open(String filename) - this gives you back an InputStream which you can then wrap in a BufferedInputStream and otherwise call read() to get the bytes. This would work regardless of the file type. What kind of problems have you had with this approach specifically?
I think you have left out the slash as in
File jfile = new File("file:///android_asset/"+filename);
There's three forward slashes, not two. :)
For me the solution was to uninistall the application, clean the project in Eclipse and run it again. The problem was Android couldn't find the new files I put in the asset folder.
I ended up reading this question so I hope this can be helpful to someone else.

Reading File In JAR using Relative Path

I have some text configuration file that need to be read by my program. My current code is:
protected File getConfigFile() {
URL url = getClass().getResource("wof.txt");
return new File(url.getFile().replaceAll("%20", " "));
}
This works when I run it locally in eclipse, though I did have to do that hack to deal with the space in the path name. The config file is in the same package as the method above. However, when I export the application as a jar I am having problems with it. The jar exists on a shared, mapped network drive Z:. When I run the application from command line I get this error:
java.io.FileNotFoundException: file:\Z:\apps\jar\apps.jar!\vp\fsm\configs\wof.txt
How can I get this working? I just want to tell java to read a file in the same directory as the current class.
Thanks,
Jonah
When the file is inside a jar, you can't use the File class to represent it, since it is a jar: URI. Instead, the URL class itself already gives you with openStream() the possibility to read the contents.
Or you can shortcut this by using getResourceAsStream() instead of getResource().
To get a BufferedReader (which is easier to use, as it has a readLine() method), use the usual stream-wrapping:
InputStream configStream = getClass().getResourceAsStream("wof.txt");
BufferedReader configReader = new BufferedReader(new InputStreamReader(configStream, "UTF-8"));
Instead of "UTF-8" use the encoding actually used by the file (i.e. which you used in the editor).
Another point: Even if you only have file: URIs, you should not do the URL to File-conversion yourself, instead use new File(url.toURI()). This works for other problematic characters as well.

Categories