如何从JavaJAR文件中读取资源文件?
我试图从一个单独的jar(作为桌面应用程序运行)中访问jar文件中的XML文件。我可以获取所需文件的URL,但当我将其作为字符串传递给FileReader时,会得到一个FileNotFoundException,表示文件名、目录名或卷标语法不正确 作为参考,我可以从同一个jar读取图像资源,并将URL传递给ImageIcon构造函数。这似乎表明我用来获取URL的方法是正确的如何从JavaJAR文件中读取资源文件?,java,jar,resources,Java,Jar,Resources,我试图从一个单独的jar(作为桌面应用程序运行)中访问jar文件中的XML文件。我可以获取所需文件的URL,但当我将其作为字符串传递给FileReader时,会得到一个FileNotFoundException,表示文件名、目录名或卷标语法不正确 作为参考,我可以从同一个jar读取图像资源,并将URL传递给ImageIcon构造函数。这似乎表明我用来获取URL的方法是正确的 URL url = getClass().getResource("/xxx/xxx/xxx/services.xml")
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文件有什么问题?您不能说这是桌面应用还是web应用。如果是桌面,我会使用相应类加载器中的getResourceAsStream方法;如果是web应用,我会使用上下文方法。在您的技术之外,为什么不使用标准Java来获取您想要的引用?从那以后,您的大多数问题都应该消失。看起来您想使用java.lang.Class.getResourceAsStreamString,请参阅
看起来您正在使用URL.toString结果作为FileReader构造函数的参数。URL.toString有点不完整,通常应该使用URL.toURI.toString。在任何情况下,字符串都不是文件路径 相反,您应该: 将URL传递给ServicesLoader,并让它调用openStream或类似程序。 使用Class.getResourceAsStream并只传递流,可能在InputSource内部。记住检查空值,因为API有点混乱。
问题是我调用XMLReader的解析方法时走得太远了。parse方法接受InputSource,因此甚至没有理由使用FileReader。将上面代码的最后一行更改为
xr.parse( new InputSource( filename ));
工作很好。
< P>如果你广泛地使用资源,你可以考虑使用。 还支持: *本地文件 *FTP,SFTP *HTTP和HTTPS *备份的临时文件 *Zip、Jar和Tar未压缩、tgz或tbz2 *gzip和bzip2 *资源 *冲压驱动 *默剧还有-但没有太多的文档。我有两个用于读取数据的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文件相同的位置。我想指出的一个问题是,如果相同的资源位于多个jar文件中会发生什么。 假设您想读取/org/node/foo.txt,但不是从一个文件中读取,而是从每个jar文件中读取 我以前曾多次遇到过同样的问题。 我希望在JDK7中有人会编写一个类路径文件系统,但遗憾的是现在还没有 Spring有一个资源类,它允许您很好地加载类路径资源 我编写了一个小原型来解决从多个jar文件中读取资源的问题。原型并不处理每一种边缘情况,但它可以处理在jar文件中的目录中查找资源的问题 我使用堆栈溢出已有相当一段时间了。这是我记得回答一个问题的第二个答案,所以如果我回答得太久,请原谅,这是我的天性 这是一个原型资源读取器。原型缺乏健壮的错误检查 我已经设置了两个原型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下都有一个名为resource.txt的文件
这只是类路径处理程序的原型://
我的本地资源中也有一个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();
}
}
}
}
}
*jar文件在/org/node/resource.txt下都有一个名为resource.txt的文件。
*
*这只是类路径处理程序的原型://
*我的本地资源中也有一个resource.foo.txt用于此项目。
*
*/
公共类路径读取器{
公共静态无效主字符串[]args引发异常{
/*这个项目包括两个jar文件,每个文件都有一个位于
在/org/node/called resource.txt中。
*/
/*
名称空间只是我用来查看文件是否在目录中的一个设备
以名称空间开始。将名称空间想象为文件扩展名
但这是文件的开始,而不是结束。
*/
字符串名称空间=资源;
//有些资源是类路径。
字符串someResource=args.length>0?args[0]:
//classpath:///org/node/resource.txt;它可以处理文件
类路径:
///组织/节点///它也适用于目录
URI someResourceURI=URI.createsomeResource;
资源的System.out.printlnURI=+someResourceURI;
someResource=someResourceURI.getPath;
资源的System.out.printlnPATH=+someResource;
布尔isDir=!someResource.endsWith.txt;
/**类路径资源永远不能真正以斜杠开始。
*逻辑上是这样,但事实上你必须去掉它。
*这是类路径资源的已知行为。
*它使用斜杠,除非资源位于jar文件中。
*底线是,剥离它,它总是有效的。
*/
如果someResource.startsWith/{
someResource=someResource.substring1;
}
/*使用类加载器查找具有此名称的所有资源。
查找与我们正在查找的位置匹配的所有资源*/
枚举资源=null;
/*首先检查上下文类加载器。如果可用,请始终使用此选项*/
试一试{
资源=
Thread.currentThread.getContextClassLoader.GetResourcesMeresource;
}捕获异常{
例如,打印跟踪;
}
如果资源==null | |!资源.hasMoreElements{
resources=ClasspathReader.class.getClassLoader.GetResourcesMereSource;
}
//现在从类路径迭代资源的URL
而resources.hasMoreElements{
URL resource=resources.nextElement;
/*如果资源是一个文件,这意味着我们可以使用正常的机制
来扫描目录。
*/
if resource.getProtocol.equals文件{
//如果它是一个文件,那么我们可以用正常的方式处理它。
handleFileresource,名称空间;
持续
}
System.out.printlnResource+资源;
/*
拆分如下所示的字符串:
jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/
进入
这个/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar
还有这个
/组织/节点/
*/
String[]split=resource.toString.split:;
字符串[]split2=split[2]。split!;
字符串zipFileName=split2[0];
字符串sresource=split2[1];
System.out.printfAfter拆分zip文件名=%s+
\nresource在zip%s中\n,zipFileName,sresource;
/*打开zip文件*/
ZipFile ZipFile=新ZipFilezipFileName;
/*遍历条目*/
枚举条目=zipFile.entries;
而entries.hasMoreElements{
ZipEntry entry=entries.nextElement;
/*如果是目录,则跳过它*/
if entry.isDirectory{
持续
}
String entryName=entry.getName;
System.out.printfzip条目名称%s\n,entryName;
/*如果它不是以someResource字符串开头
那就不是我们的资源了,继续吧。
*/
if!entryName.startsWithsomeResource{
持续
}
/*文件名是条目名称的一部分。
*其中/foo/bar/foo/bee/bar.txt是文件
*/
字符串文件名=entryName.substringentryName.lastIndexOf/+1;
System.out.printfielname%s\n,文件名;
/*查看文件是否以名称空间开始,以扩展名结束。
*/
如果fileName.startsWithnamespace&&fileName.endsWith.txt{
/*如果找到文件,请打印出来
将文件的内容保存到System.out*/
try Reader Reader=new InputStreamReaderzipFile.getInputStreamentry{
StringBuilder=新的StringBuilder;
int ch=0;
而ch=reader.read!=-1{
builder.appendch;
}
System.out.printfzip文件名=%s\n\n\n文件%s的内容\n\n,entryName,builder;
}捕获异常{
例如,打印跟踪;
}
}
//使用条目查看是否是文件“1.txt”
//使用file.getInputStreamentry读取字节
}
}
}
/**
*该文件在文件系统中不是zip文件,
*这是为了这个例子的完整性。
*否则。
*
*@param资源
*@param名称空间
*@抛出异常
*/
私有静态void handleFileURL资源,字符串命名空间引发异常{
System.out.println将此资源作为文件+资源处理;
URI=resource.toURI;
File File=new Fileuri.getPath;
if file.isDirectory{
对于文件childFile:File.listFiles{
if childFile.isDirectory{
持续
}
字符串文件名=childFile.getName;
如果fileName.startsWithnamespace&&fileName.endsWithtxt{
尝试FileReader=newFileReaderChildFile{
StringBuilder=新的StringBuilder;
int ch=0;
而ch=reader.read!=-1{
builder.appendch;
}
System.out.printfielname=%s\n\n\n文件%s的内容\n\n,childFile,builder;
}捕获异常{
例如,打印跟踪;
}
}
}
}否则{
字符串文件名=file.getName;
如果fileName.startsWithnamespace&&fileName.endsWithtxt{
try FileReader=newfilereader文件{
StringBuilder=新的StringBuilder;
int ch=0;
而ch=reader.read!=-1{
builder.appendch;
}
System.out.printfielname=%s\n\n\n文件%s的内容\n\n,文件名,生成器;
}捕获异常{
例如,打印跟踪;
}
}
}
}
}
下面是一个示例代码,介绍如何正确读取jar文件中的文件(在本例中为当前正在执行的jar文件) 只要用jar文件的路径更改可执行文件,如果它不是当前正在运行的文件 然后将filePath更改为要在jar文件中使用的文件的路径。例如,如果您的文件在 someJar.jar\img\test.gif 。将文件路径设置为img\test.gif
@JVerstry您的回答对我很有帮助,遗憾的是,它被完全低估了。您可以发布工作代码吗?请确保在使用文件API时,使用unix文件路径分隔符。
File executable = new File(BrowserViewControl.class.getProtectionDomain().getCodeSource().getLocation().toURI());
JarFile jar = new JarFile(executable);
InputStream fileInputStreamReader = jar.getInputStream(jar.getJarEntry(filePath));
byte[] bytes = new byte[fileInputStreamReader.available()];
int sizeOrig = fileInputStreamReader.available();
int size = fileInputStreamReader.available();
int offset = 0;
while (size != 0){
fileInputStreamReader.read(bytes, offset, size);
offset = sizeOrig - fileInputStreamReader.available();
size = fileInputStreamReader.available();
}