我正在尝试从作为桌面应用程序运行的单独jar中访问jar文件中的XML文件。我可以获取我需要的文件的URL,但是当我将它传递给FileReader(作为String)时,我得到一个FileNotFoundException,说“文件名,目录名或卷标语法不正确。”
作为参考,我可以毫不费力地从同一个jar中读取图像资源,并将URL传递给ImageIcon构造函数。这似乎表明我用来获取URL的方法是正确的。
URL url = getClass().getResource("/xxx/xxx/xxx/services.xml");
ServicesLoader jsl = new ServicesLoader( url.toString() );
在ServicesLoader类中,我有
XMLReader xr = XMLReaderFactory.createXMLReader();
xr.setContentHandler( this );
xr.setErrorHandler( this );
xr.parse( new InputSource( new FileReader( filename )));
使用此技术读取XML文件有什么问题?
答案 0 :(得分:59)
您希望使用java.lang.Class.getResourceAsStream(String)
,请参阅
http://java.sun.com/javase/6/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)
答案 1 :(得分:5)
您没有说这是桌面应用还是网络应用。我会使用适当的ClassLoader中的getResourceAsStream()
方法(如果它是桌面)或上下文(如果它是Web应用程序)。
答案 2 :(得分:4)
看起来好像使用URL.toString
结果作为FileReader
构造函数的参数。 URL.toString
有点破,相反,您通常应该使用url.toURI().toString()
。在任何情况下,字符串都不是文件路径。
相反,您应该:
URL
传递给ServicesLoader
并让其致电openStream
或类似。Class.getResourceAsStream
并将流传递过来,可能在InputSource
内。 (请记住检查空值,因为API有点乱。)答案 3 :(得分:4)
问题是我在调用XMLReader的parse方法方面走得太远了。 parse方法接受一个InputSource,因此没有理由使用FileReader。将上面代码的最后一行更改为
xr.parse( new InputSource( filename ));
工作正常。
答案 4 :(得分:2)
我想指出,如果相同的资源存在于多个jar文件中,则会出现一个问题。 假设您想要读取/org/node/foo.txt,但不是从一个文件读取,而是从每个jar文件中读取。
之前我曾多次遇到同样的问题。 我希望在JDK 7中有人会写一个类路径文件系统,但还没有。
Spring有Resource类,它允许你很好地加载类路径资源。
我写了一个小原型来解决从多个jar文件中读取资源这个问题。原型不处理每个边缘情况,但它确实处理在jar文件中的目录中寻找资源。
我已经使用Stack Overflow很长一段时间了。这是我记得回答一个问题的第二个答案,如果我走得太久就会原谅我(这是我的本性)。
这是原型资源阅读器。原型没有强大的错误检查。
我已经设置了两个原型jar文件。
<pre>
<dependency>
<groupId>invoke</groupId>
<artifactId>invoke</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>node</groupId>
<artifactId>node</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
每个jar文件都有/ org / node /下的文件,名为resource.txt。
这只是一个处理程序与classpath一样的原型:// 我在这个项目的本地资源中也有一个resource.foo.txt。
它将它们全部拾取并打印出来。
package com.foo;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URL;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Prototype resource reader.
* This prototype is devoid of error checking.
*
*
* I have two prototype jar files that I have setup.
* <pre>
* <dependency>
* <groupId>invoke</groupId>
* <artifactId>invoke</artifactId>
* <version>1.0-SNAPSHOT</version>
* </dependency>
*
* <dependency>
* <groupId>node</groupId>
* <artifactId>node</artifactId>
* <version>1.0-SNAPSHOT</version>
* </dependency>
* </pre>
* The jar files each have a file under /org/node/ called resource.txt.
* <br />
* This is just a prototype of what a handler would look like with classpath://
* I also have a resource.foo.txt in my local resources for this project.
* <br />
*/
public class ClasspathReader {
public static void main(String[] args) throws Exception {
/* This project includes two jar files that each have a resource located
in /org/node/ called resource.txt.
*/
/*
Name space is just a device I am using to see if a file in a dir
starts with a name space. Think of namespace like a file extension
but it is the start of the file not the end.
*/
String namespace = "resource";
//someResource is classpath.
String someResource = args.length > 0 ? args[0] :
//"classpath:///org/node/resource.txt"; It works with files
"classpath:///org/node/"; //It also works with directories
URI someResourceURI = URI.create(someResource);
System.out.println("URI of resource = " + someResourceURI);
someResource = someResourceURI.getPath();
System.out.println("PATH of resource =" + someResource);
boolean isDir = !someResource.endsWith(".txt");
/** Classpath resource can never really start with a starting slash.
* Logically they do, but in reality you have to strip it.
* This is a known behavior of classpath resources.
* It works with a slash unless the resource is in a jar file.
* Bottom line, by stripping it, it always works.
*/
if (someResource.startsWith("/")) {
someResource = someResource.substring(1);
}
/* Use the ClassLoader to lookup all resources that have this name.
Look for all resources that match the location we are looking for. */
Enumeration resources = null;
/* Check the context classloader first. Always use this if available. */
try {
resources =
Thread.currentThread().getContextClassLoader().getResources(someResource);
} catch (Exception ex) {
ex.printStackTrace();
}
if (resources == null || !resources.hasMoreElements()) {
resources = ClasspathReader.class.getClassLoader().getResources(someResource);
}
//Now iterate over the URLs of the resources from the classpath
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
/* if the resource is a file, it just means that we can use normal mechanism
to scan the directory.
*/
if (resource.getProtocol().equals("file")) {
//if it is a file then we can handle it the normal way.
handleFile(resource, namespace);
continue;
}
System.out.println("Resource " + resource);
/*
Split up the string that looks like this:
jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/
into
this /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar
and this
/org/node/
*/
String[] split = resource.toString().split(":");
String[] split2 = split[2].split("!");
String zipFileName = split2[0];
String sresource = split2[1];
System.out.printf("After split zip file name = %s," +
" \nresource in zip %s \n", zipFileName, sresource);
/* Open up the zip file. */
ZipFile zipFile = new ZipFile(zipFileName);
/* Iterate through the entries. */
Enumeration entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
/* If it is a directory, then skip it. */
if (entry.isDirectory()) {
continue;
}
String entryName = entry.getName();
System.out.printf("zip entry name %s \n", entryName);
/* If it does not start with our someResource String
then it is not our resource so continue.
*/
if (!entryName.startsWith(someResource)) {
continue;
}
/* the fileName part from the entry name.
* where /foo/bar/foo/bee/bar.txt, bar.txt is the file
*/
String fileName = entryName.substring(entryName.lastIndexOf("/") + 1);
System.out.printf("fileName %s \n", fileName);
/* See if the file starts with our namespace and ends with our extension.
*/
if (fileName.startsWith(namespace) && fileName.endsWith(".txt")) {
/* If you found the file, print out
the contents fo the file to System.out.*/
try (Reader reader = new InputStreamReader(zipFile.getInputStream(entry))) {
StringBuilder builder = new StringBuilder();
int ch = 0;
while ((ch = reader.read()) != -1) {
builder.append((char) ch);
}
System.out.printf("zip fileName = %s\n\n####\n contents of file %s\n###\n", entryName, builder);
} catch (Exception ex) {
ex.printStackTrace();
}
}
//use the entry to see if it's the file '1.txt'
//Read from the byte using file.getInputStream(entry)
}
}
}
/**
* The file was on the file system not a zip file,
* this is here for completeness for this example.
* otherwise.
*
* @param resource
* @param namespace
* @throws Exception
*/
private static void handleFile(URL resource, String namespace) throws Exception {
System.out.println("Handle this resource as a file " + resource);
URI uri = resource.toURI();
File file = new File(uri.getPath());
if (file.isDirectory()) {
for (File childFile : file.listFiles()) {
if (childFile.isDirectory()) {
continue;
}
String fileName = childFile.getName();
if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {
try (FileReader reader = new FileReader(childFile)) {
StringBuilder builder = new StringBuilder();
int ch = 0;
while ((ch = reader.read()) != -1) {
builder.append((char) ch);
}
System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", childFile, builder);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
} else {
String fileName = file.getName();
if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {
try (FileReader reader = new FileReader(file)) {
StringBuilder builder = new StringBuilder();
int ch = 0;
while ((ch = reader.read()) != -1) {
builder.append((char) ch);
}
System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", fileName, builder);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
答案 5 :(得分:0)
在您的技术之外,为什么不使用标准Java JarFile class来获取您想要的参考?从那里你的大部分问题都应该消失。
答案 6 :(得分:0)
如果您广泛使用资源,可以考虑使用 Commons VFS
还支持: *本地文件 * FTP,SFTP * HTTP和HTTPS *临时文件&#34;正常FS支持) * Zip,Jar和Tar(未压缩,tgz或tbz2) * gzip和bzip2 *资源 * ram - &#34; ramdrive&#34; * mime
还有JBoss VFS - 但记录不多。
答案 7 :(得分:0)
我有两个用于读取数据的CSV文件。 java程序导出为可运行的jar文件。导出它时,您会发现它不会导出您的资源。
我在eclipse中名为data的项目下添加了一个文件夹。在该文件夹中,我存储了我的csv文件。
当我需要引用这些文件时,我会这样做......
private static final String ZIP_FILE_LOCATION_PRIMARY = "free-zipcode-database-Primary.csv";
private static final String ZIP_FILE_LOCATION = "free-zipcode-database.csv";
private static String getFileLocation(){
String loc = new File("").getAbsolutePath() + File.separatorChar +
"data" + File.separatorChar;
if (usePrimaryZipCodesOnly()){
loc = loc.concat(ZIP_FILE_LOCATION_PRIMARY);
} else {
loc = loc.concat(ZIP_FILE_LOCATION);
}
return loc;
}
然后当您将jar放在某个位置以便可以通过命令行运行时,请确保将包含资源的数据文件夹添加到与jar文件相同的位置。
答案 8 :(得分:0)
下面是示例代码,说明如何正确读取jar文件(在本例中为当前正在执行的jar文件)
如果不是当前正在运行的jar文件,只需更改jar文件的路径即可。
然后将filePath更改为要在jar文件中使用的文件的路径。即如果您的文件位于
someJar.jar \ img \ test.gif
。将filePath设置为“ img \ test.gif”
import os
# to get the location of the current python file
basedir = os.path.dirname(os.path.abspath(__file__))
# to join it with the filename
categorization_file = os.path.join(basedir,'ExcelFile.xlsx')