更有效地从 Jar 中提取文件
我正在扩展一个实用程序类,该类捆绑了一组图像和 .xml 描述文件。 目前,我将所有文件保存在一个目录中并从那里加载它们。 该目录如下所示:
8.png
8.xml
9.png
9.xml
10.png
10.xml
...
...
50.png
50.xml
...
这是我当前的构造函数。 它速度快如闪电,可以完成我需要它做的事情。 (我删除了一些错误检查以使其更易于阅读):
public DivineFont(String directory ) {
File dir = new File(directory);
//children is an array that looks like this: '10.fnt', '11.fnt', etc.
String[] children = dir.list(fntFileFilter);
fonts = new Hashtable<Integer, AngelCodeFont>(100);
AngelCodeFont buffer;
int number;
String fntFile;
String imgFile;
for(int k = 0; k < children.length; k++ ) {
number = Integer.parseInt( children[k].split("\\.")[0] );
fntFile = directory + File.separator + number + ".xml";
imgFile = directory + File.separator + number + ".png";
buffer = new AngelCodeFont(fntFile, imgFile);
fonts.put(number, buffer);
}
}
为了网络启动和清洁,我一直在尝试从 Jar 加载这些资源。 我已经让它工作了,但是加载时间从瞬时变成了几秒钟,这是不可接受的。 这是我尝试过的代码(再次,删除了错误检查):(
这不是做我想做的事情的最佳方法,它是一个模型,用于查看这个想法是否有效。它没有两个 for 循环绝不是问题的根源;它是创建所有这些输入流的过程,这会减慢速度)
public DivineFont(String jarFileName ) {
JarFile jarfile = new JarFile(jarFileName);
Enumeration<JarEntry> em = jarfile.entries();
ArrayList<Integer> fontHeights = new ArrayList<Integer>(100);
for (Enumeration em1 = jarfile.entries(); em1.hasMoreElements(); ) {
String fileName = em1.nextElement().toString();
if( fileName.endsWith(".fnt") ) {
fontHeights.add( Integer.parseInt(fileName.split("\\.")[0] ) );
}
}
fonts = new Hashtable<Integer, AngelCodeFont>(100);
AngelCodeFont buffer;
int number;
for(int k = 0; k < fontHeights.size(); k++ ) {
number = fontHeights.get(k);
InputStream fntFileStream = jarfile.getInputStream(jarfile.getEntry(number + ".xml"));
InputStream pngFileStream = jarfile.getInputStream(jarfile.getEntry(number + ".png"));
buffer = new AngelCodeFont(String.valueOf(number), fntFileStream, pngFileStream );
fonts.put(number, buffer);
}
}
除了我的方法之外,任何人都知道使用 .jar 文件的更好方法。这里试过吗? 这是 AngelCodeFont API。 如果绝对有必要,我可以为此提交补丁,但我宁愿不必这样做。 在我看来,可能有一种方法可以做我想做的事,只是我不熟悉它。
我并不是非常反对将 jar 快速转储到临时目录,然后从那里读取文件,但如果有一种方法可以快速直接从 jar 中读取文件,我宁愿这样做。
另外:压缩根本不是问题。 我使用罐子的唯一原因是包装问题。
I'm extending a utility class that bundles a set of images and .xml description files. Currently I keep all the files in a directory and load them from there. The directory looks like this:
8.png
8.xml
9.png
9.xml
10.png
10.xml
...
...
50.png
50.xml
...
Here's my current constructor. It is lightning fast and does what I need it to do. (I've stripped out some of the error checking to make it easier to read):
public DivineFont(String directory ) {
File dir = new File(directory);
//children is an array that looks like this: '10.fnt', '11.fnt', etc.
String[] children = dir.list(fntFileFilter);
fonts = new Hashtable<Integer, AngelCodeFont>(100);
AngelCodeFont buffer;
int number;
String fntFile;
String imgFile;
for(int k = 0; k < children.length; k++ ) {
number = Integer.parseInt( children[k].split("\\.")[0] );
fntFile = directory + File.separator + number + ".xml";
imgFile = directory + File.separator + number + ".png";
buffer = new AngelCodeFont(fntFile, imgFile);
fonts.put(number, buffer);
}
}
For the sake of webstart and cleanliness, I've been trying to load these resources from a Jar instead. I've got it working, but the load time went from instantaneous to a few seconds, and that's not acceptable. Here's the code I tried (again, error checking stripped):
(This isn't the best way to do what I want to do, it's a mock-up to see if the idea worked. It didn't. The two for-loops is in no way the source of the problem; it's the process of creating all those InputStreams that slows it down)
public DivineFont(String jarFileName ) {
JarFile jarfile = new JarFile(jarFileName);
Enumeration<JarEntry> em = jarfile.entries();
ArrayList<Integer> fontHeights = new ArrayList<Integer>(100);
for (Enumeration em1 = jarfile.entries(); em1.hasMoreElements(); ) {
String fileName = em1.nextElement().toString();
if( fileName.endsWith(".fnt") ) {
fontHeights.add( Integer.parseInt(fileName.split("\\.")[0] ) );
}
}
fonts = new Hashtable<Integer, AngelCodeFont>(100);
AngelCodeFont buffer;
int number;
for(int k = 0; k < fontHeights.size(); k++ ) {
number = fontHeights.get(k);
InputStream fntFileStream = jarfile.getInputStream(jarfile.getEntry(number + ".xml"));
InputStream pngFileStream = jarfile.getInputStream(jarfile.getEntry(number + ".png"));
buffer = new AngelCodeFont(String.valueOf(number), fntFileStream, pngFileStream );
fonts.put(number, buffer);
}
}
Anyone know of a better way to work with .jar files besides the way I've tried here? Here's the AngelCodeFont API. If it was absolutely necessary I could submit a patch for that, but I'd rather not have to. It seems to me that there's probably a way to do what I want to do, I'm just not familiar with it.
I'm not terribly against quickly dumping the jar to a temporary directory and then reading the files from there, but if there's a way to do it reading directly from the jar quickly, I'd much rather do that.
Also: Compression isn't an issue at all. The only reason I'm using a jar is for the packing issue.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
打开 JAR 是一项非常昂贵的操作。 因此,您可能希望打开 JAR 一次并将 JarFile 实例保留在某处的静态字段中。 就您而言,您还需要读取所有条目并将它们保存在哈希图中,以便即时访问您需要的资源。
另一个解决方案是将 JAR 放在类路径上并使用
“/”! 如果省略它,Java 将在找到文件divinefont.class 的同一目录(包)中进行搜索。
从类路径获取数据在 Java 中已经被优化到了极致。
Opening a JAR is a very expensive operation. So you may want to open the JAR once and keep the
JarFile
instance in a static field somewhere. In your case, you will also want to read all entries and keep them in a hashmap for instant access of the resource you need.Another solution is to put the JAR on the classpath and use
Mind the "/"! If you omit it, Java will search in the same directory (package) in which it finds the file DivineFont.class.
Getting things from the classpath has been optimized to death in Java.
嗯,我找到了答案。 我原来的问题的答案是“错误的问题”。 Jar 文件不是问题,而是我用来加载图像的库。
当我从文件系统加载时,图像被命名为“38.png”等。当我从 Jar 加载时,我只是将其命名为“38”。
库内的图像加载器类使用名称的文件扩展名来标识要使用的图像加载器。 如果没有文件扩展名,它将使用较慢的基本图像加载器。 当我将这句话改为:
我们
自己是赢家。
不管怎样,谢谢你们的帮助。 我花了几个小时才弄清楚这一点,但如果不是你的帖子,我可能会继续认为责任是 Java 的而不是我正在使用的库。
Well, I found the answer. The answer to my original question was "Wrong question." The Jar file wasn't the issue, it was the library I was using to load the images.
When I was loading from the file system, the image was being named "38.png", etc. When I was loading from the Jar, I was simply naming it "38".
The Image loader class inside the library uses the file extension of the name to identify which image loader to use. If there is no file extension, it uses a slower, basic image loader. When I changed this line:
to this line:
We have ourselves a winner.
Thanks for the help anyway guys. It took me a few hours to figure this out, but if not for your posts, I probably would have continued to assume the blame was Java's rather than the Library I'm using.
Jar 文件操作可能非常昂贵,并且 Java 类库已经实现了这种资源加载(可能尽可能高效)。
此外,一旦您进入 webstart,您的 jar 文件名将被破坏,因此您可能会最终必须探索类路径上的每个 jar 文件来加载资源(我已经做到了!丑陋!)
相反,使用 Class.getResourceAsStream(String resourceName)。 我还没有对其进行分析,但我没有注意到它比直接文件访问明显慢。
Jar file manipulation can be very expensive, and the Java class library already implements this kind of resource loading (probably as efficiently as possible.)
Plus, once you're in webstart, your jar file names will get mangled, so you'll likely end up having to explore every jar file on the classpath to load your resources (I've done it! Ugly!)
Instead, use Class.getResourceAsStream(String resourceName). I haven't profiled it, but I haven't noticed it being noticeably slower than direct file access.
这个库可能无法压缩到您需要的程度,但它会更快...
This library may not compress as much as you need, but it'll be faster...