I have a Swing app that I'm trying to package up in a runnable JAR file. Part of its DAO functionality is reading and writing in CSV format to a .dat file inside src/main/resources/dictData.dat
My issue is that each time I try to run the jar, I get
java.io.FileNotFoundException: file:/Users/jason/projects/test-dict/target/
dictionary-jar-with-dependencies.jar!/dictData.dat
(No such file or directory)
from the command line. This is from a jar built via mvn package
and the maven-assembly-plugin
specifications
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.test.dictionary.init.AppInit</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
I have a FileIO class implementation that handles the reading and writing to the file.
public class FileIO implements IO{
private static final String DICTIONARYFILE = "dictData.dat";
private File dataFile;
private Writer dataWriter;
private Reader dataReader;
@Override
public Map<String, Word> loadDataFile(){
ClassLoader classLoader = getClass().getClassLoader();
Map<String, Word> dictMap = new HashMap<>();
try {
//Open file connection and read stream
dataFile = new File(classLoader.getResource(DICTIONARYFILE).getFile());
dataReader = new FileReader(dataFile);
//CSV parsing code here
} catch (NullPointerException | IOException | NumberFormatException e){
e.printStackTrace();
}
}
}
I only get this error when executing
java -jar dictionary-jar-with-dependencies.jar
inside /Users/jason/projects/test-dict/target/
. If I run via the IDE, I get no errors.
Thing is, I can see dictData.dat
at the end of the jar via vim dictionary-jar-with-dependencies.jar
:
...
...
com/test/dictionary/utils/FileIO.class
com/test/dictionary/utils/HttpUtils.class
com/test/dictionary/utils/IO.class
dictData.dat
META-INF/maven/com.test/
META-INF/maven/com.test/dictionary/
META-INF/maven/com.test/dictionary/pom.xml
META-INF/maven/com.test/dictionary/pom.properties
So, short of moving the data file into a test-dict/com/text/dictionary/data
location, how can I resolve this issue?
You can't convert the URL returned by classLoader.getResource(DICTIONARYFILE)
to a file name because an application resource usually isn't an actual file. Even if you could, URL.getFile()
is the wrong way to do it. Use this instead:
URL dataFile = classLoader.getResource(DICTIONARYFILE);
dataReader = new InputStreamReader(dataFile.openStream());
Detailed explanation:
When your program is running from a .jar file (which nearly all programs do, except in some development environments), the Class.getResource and ClassLoader.getResource methods return a URL which is a jar URL . A jar URL is a Java-specific URL scheme with this format:
jar:
url-of-jarfile!
path-of-jar-entry
In your case, the .jar file was located at /Users/jason/projects/test-dict/target/dictionary-jar-with-dependencies.jar
. In URL form, that is file:/Users/jason/projects/test-dict/target/dictionary-jar-with-dependencies.jar
.
The entry within that jar file which you requested /dictData.dat
. So the URL returned by your getResource
call was:
jar:file:/Users/jason/projects/test-dict/target/dictionary-jar-with-dependencies.jar!/dictData.dat
URL.getFile()
does not do what you think it does. In particular, URL.getFile()
does not convert a URL into a filename. It merely returns the path portion of a URL. The path portion is not a filename; it is just whatever part of the URL comes after the scheme/authority/host/port, up to first question mark ('?') or hash ('#').
In the case of a jar URL, the path portion is everything that comes after the 4 characters jar:
. So you effectively called new File("file:/Users/jason/projects/test-dict/target/dictionary-jar-with-dependencies.jar!/dictData.dat")
.
To read from a URL, you should not try to convert it to a file and you should not assume the URL is a file: URL. As you have seen, it often is not. Read from the InputStream returned by the URL's openStream()
method instead.
If you're wondering why a method named "getFile" does not actually return a file, the reason is that java.net.URL is a very old class. It was present in Java 1.0, way back in the mid-90s. At that time, most URLs did in fact point to physical files, especially ftp:
URLs, which were still far more common than http:
URLs. The java.net.URI class is newer and uses more accurate terminology. (Every URL is also a URI.)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.