java 从jar包中读取资源文件

转载自  java 从jar包中读取资源文件

在代码中读取一些资源文件(比如图片,音乐,文本等等),在集成环境(Eclipse)中运行的时候没有问题。但当打包成一个可执行的jar包(将资源文件一并打包)以后,这些资源文件找不到,如下代码:

//源代码1:    
package edu.hxraid;    
import java.io.*;    
public class Resource {    public  void getResource() throws IOException{    File file=new File("bin/resource/res.txt");    BufferedReader br=new BufferedReader(new FileReader(file));    String s="";    while((s=br.readLine())!=null)    System.out.println(s);    }    
}      

这段代码写在Eclipse建立的java Project中,其目录为:(其中将资源文件res.txt放在了bin目录下,以便打成jar包)

1、src/
      src/edu/hxraid/Resource.java
 2、bin/
      bin/resource/res.txt

      bin/edu/hxraid/Resource.class

很显然运行源代码1是能够找到资源文件res.txt。但当我们把整个工程打成jar包以后(ResourceJar.jar),这个jar包内的目录为:

edu/hxraid/Resource.class
resource/res.txt
 
而这时jar包中Resource.class字节码:ldc <String "bin/resource/res.txt"> [20] 将无法定位到jar包中的res.txt位置上。就算把bin/目录去掉:ldc <String "resource/res.txt"> [20] 仍然无法定位到jar包中res.txt上。
这主要是因为jar包是一个单独的文件而非文件夹,绝对不可能通过"file:/e:/.../ResourceJar.jar/resource /res.txt"这种形式的文件URL来定位res.txt。所以即使是相对路径,也无法定位到jar文件内的txt文件(读者也许对这段原因解释有些费解,在下面我们会用一段代码运行的结果来进一步阐述)。
 
那么把资源打入jar包,无论ResourceJar.jar在系统的什么路径下,jar包中的字节码程序都可以找到该包中的资源。这会是幻想吗?


当然不是,我们可以用类装载器(ClassLoader)来做到这一点:
(1) ClassLoader 是类加载器的抽象类。它可以在运行时动态的获取加载类的运行信息。 可以这样说,当我们调用ResourceJar.jar中的Resource类时,JVM加载进Resource类,并记录下Resource运行时信息(包括Resource所在jar包的路径信息)。而ClassLoader类中的方法可以帮助我们动态的获取这些信息:
          ● public URL getResource(String name) 
            查找具有给定名称的资源。资源是可以通过类代码以与代码基无关的方式访问的一些数据(图像、声音、文本等)。并返回资源的URL对象。
          ● public InputStream getResourceAsStream(String name); 
             返回读取指定资源的输入流。这个方法很重要,可以直接获得jar包中文件的内容。
         

 (2) ClassLoader是abstract的,不可能实例化对象,更加不可能通过ClassLoader调用上面两个方法。所以我们真正写代码的时候,是通过Class类中的getResource()和getResourceAsStream()方法,这两个方法会委托ClassLoader中的getResource()和getResourceAsStream()方法 。好了,现在我们重新写一段Resource代码,来看看上面那段费解的话是什么意思了:

//源代码2:    
package edu.hxraid;    
import java.io.*;    
import java.net.URL;    
public class Resource {    public  void getResource() throws IOException{      //查找指定资源的URL,其中res.txt仍然开始的bin目录下     URL fileURL=this.getClass().getResource("/resource/res.txt");     System.out.println(fileURL.getFile());    }    public static void main(String[] args) throws IOException {    Resource res=new Resource();    res.getResource();    }    
}    
运行这段源代码结果:/E:/Code_Factory/WANWAN/bin/resource/res.txt  (../ Code_Factory/WANWAN/.. 是java project所在的路径)


我们将这段代码打包成ResourceJar.jar ,并将ResourceJar.jar放在其他路径下(比如 c:\ResourceJar.jar)。然后另外创建一个java project并导入ResourceJar.jar,写一段调用jar包中Resource类的测试代码:

import java.io.IOException;    
import edu.hxraid.Resource;    
public class TEST {    public static void main(String[] args) throws IOException {    Resource res=new Resource();    res.getResource();    }    
}    
这时的运行结果是:file:/C:/ResourceJar.jar!/resource/res.txt
我们成功的在运行时动态获得了res.txt的位置。然而,问题来了,你是否可以通过下面这样的代码来得到res.txt文件?
File f=new File("C:/ResourceJar.jar!/resource/res.txt");
当然不可能,因为".../ResourceJar.jar!/resource/...."并不是文件资源定位符的格式 (jar中资源有其专门的URL形式: jar:<url>!/{entry} )。所以,如果jar包中的类源代码用File f=new File(相对路径);的形式,是不可能定位到文件资源的。这也是为什么源代码1打包成jar文件后,调用jar包时会报出FileNotFoundException的症结所在了。


 注: 我们可以通过 new ImageIcon(类.class.getResource("XX.gif"))获得ImageIcon对象,但如果你要获得jar文件中的资源文件流,上面方法行不通。

 (3) 我们不能用常规操作文件的方法来读取ResourceJar.jar中的资源文件res.txt,但可以通过Class类的getResourceAsStream()方法来获取 ,这种方法是如何读取jar中的资源文件的,这一点对于我们来说是透明的。我们将Resource.java改写成

//源代码3:    
package edu.hxraid;    
import java.io.*;    
public class Resource {    public void getResource() throws IOException{    //返回读取指定资源的输入流    InputStream is=this.getClass().getResourceAsStream("/resource/res.txt");       //InputStream is=当前类.class.getResourceAsStream("XX.config");     BufferedReader br=new BufferedReader(new InputStreamReader(is));    String s="";    while((s=br.readLine())!=null)    System.out.println(s);    }    
}    
           我们将java工程下/bin目录中的edu/hxraid/Resource.class和资源文件resource/res.txt一并打包进ResourceJar.jar中,不管jar包在系统的任何目录下,调用jar包中的Resource类都可以获得jar包中的res.txt资源,再也不会找不到res.txt文件了。

 (4) 上面方法只提供了从jar中读资源文件功能,没有提供改写jar中资源文件方法,

    通过URL、URLConnection来得到jar中资源文件的输入流(InputStream)和输出流 (OutputStream),不仅可以从jar中读取资源文件而且还可以改写jar中资源文件

URL url = 类.class.getResource("XX.config");  
URLConnection  urlConnection=url.openConnection();  
//读资源文件  
InputStream inputStream=urlConnection.getInputStream();  
BufferedReader in=new BufferedReader(new InputStreamReader(inputStream,"utf-8"));  
//写资源文件  
OutputStream outputStream=urlConnection.getOutputStream();  
PrintStream printStream=new PrintStream(outputStream);  

补充:刚测试了一下,利用URL、URLConnection来得到jar中资源文件的输出流 (OutputStream)不好使,提示java.net.UnknownServiceException: protocol doesn't support output,由于上述改写jar中资源文件方法不好使。

      我们可以换个角度,不要死扣非得读写jar中的资源文件,只有当软件第一次启动程序时才读jar中的资源文件,并将其备份到用户主目录或用户当前目录中,以后读写用户目录中的资源文件即可。首先我们把资源文件打包到jar中,程序第一次程序启动时,仍然从jar中读资源文件信息(显然可以实现),并将所读取的信息保存到内存中(如:每次启动程序时,都将资源文件信息读到List或Map集合中保存),程序运行期间对资源文件的更新都是对List和Map的更新,当第一次关闭程序时,才将内存中保存的资源信息写到System.getProperty("user.home")用户主目录(或System.getProperty("user.dir")用户当前目录)中,并生成一个与jar中资源文件一模一样的文件,以后程序启动时都从用户目录的资源文件中读写。只有当用户目录中的资源文件不存在时(这里程序可以判断一下),才从jar中的资源文件读信息,因此jar中的资源文件只是起到原始备份作用。

       当然,这样做会有2个问题(1):就是每次程序安装在一新机器上时,读取的资源文件信息都是jar中的原始资源信息,中途我们可能更新过,解决方法:将更新的资源文件再一次打包到jar中(2):由于每次对资源文件信息的更新都是对对应的内存数据的更新,只有在关闭程序时才写回到资源文件中,没有随改随写,这样就可能造成这样一种后果,如果程序不是很健壮,程序中途bug并无响应,此时只能强制关闭,务必造成更新的资源信息没有写回到资源文件中!!!解决方案:每次对内存中资源信息的更新后马上写回到文件中,利:保证了程序bug后资源文件仍然是最新的,弊:这样会造成频繁写文件操作,程序性能降低,(也不一定哦,试具体程序来看,由于它是资源文件,一般不会更新很频繁)。

(5)利用java.util.jar.JarFile没试过

        从一个jar文件读取内容,然后写回一个jar文件。首先通过一个JarFile file = new JarFile(fileName);然后file.entries()可以得到一个jar文件内部元素的迭代器。同过这个迭代器我们可以获得所有的JarEntry,然后通过InputStream is = jarFile.getInputStream(jarEntry);可以获得这个JarEntry的输入流。最后将is的内容同过JarOutputStream out = new JarOutputStream(new FileOutputStream(distJar));的out的out.write(int data)方法写入目标jar文件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328715.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

json常用的转换

一、json字符串与map的相互转换 首先要导入依赖&#xff0c;用的是fastjson依赖 <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.45</version> </dependency>json转map //第一…

JWT【JSON Web Token】 简述

JWT全称JSON Web Token[http://www.jwt.io/]&#xff0c;用于发送可通过数字签名和认证的东西&#xff0c;它包含一个紧凑的&#xff0c;URL安全的JSON对象&#xff0c;服务端可通过解析该值来验证是否有操作权限&#xff0c;是否过期等安全性检查。由于其紧凑的特点&#xff0…

Android增加自定义监听事件

适配器中 public interface ShippingDelOnClickListener {public void onClickListenerShippingDel(int position); }private ShippingDelOnClickListener mShippingDelOnClickListener;public void setShippingDelOnClickListener(ShippingDelOnClickListener shippingDelO…

BufferedInputStream与ImageInputStream

转载自 BufferedInputStream与ImageInputStreamBufferedInputStream读取图片文件&#xff08;jpg格式&#xff09;,但是图片大小在1M到200M之间都有&#xff0c;由于图片太大&#xff0c;不知道怎么处理好&#xff0c;谢谢各位大哥大姐教教小弟&#xff0c;现在只能读取1M以下读…

List自定义对象集合转List<Map<String,Object>>对象

一、List自定义对象集合转List<Map<String,Object>>对象 public static Map<String,Object> object2Map(Object obj) throws Exception{Map<String,Object> map new HashMap<String, Object>(16);Field[] fields obj.getClass().getDeclaredF…

又一大波笑到肾抽筋,笑出六块腹肌的段子

文章来源于网络&#xff0c;侵删 01 我有一个要强的妈妈&#xff0c;记得小时候&#xff0c;我妈骑自行车带我&#xff0c;我脚卡车轮里了&#xff0c;我妈感觉蹬不动&#xff0c;就站起来蹬。 长到两米算我输 02 “单身两年以上是什么体验&#xff1f;”&#xff0c; “单身20…

登录工程:传统 Web 应用中的身份验证技术

标题中 “传统 Web 应用” 这一说法也并没有什么官方定义&#xff0c;只是为了与“现代化 Web 应用”形成比较而自拟的一个概念。所谓现代化 Web 应用指的是那些基于分布式架构思想设计的&#xff0c;面向多个端提供稳定可靠的高可用服务&#xff0c;并且在需要时能够横向扩展的…

getSerializableExtra

Activity之间通过Intent传递值&#xff0c;支持基本数据类型和String对象及它们的数组对象byte、byte[]、char、char[]、boolean、boolean[]、short、short[]、int、int[]、long、long[]、float、float[]、double、double[]、String、String[]&#xff0c;还有实现Serializable…

Java 读写图像

转载自 Java 读写图像Java中进行图像I/O&#xff08;即读图片和写图片&#xff0c;不涉及到复杂图像处理&#xff09;有三个方法&#xff1a;1. Java Image I/O API&#xff0c;支持常见图片&#xff0c;从Java 2 version 1.4.0开始就内置了。主页&#xff1a;http://java.sun…

powerdesigner在工作时用到的方法

一、所有表中name同步comment的方法&#xff1a; 操作步骤&#xff1a;工具>execute commands>Edit/Run script Option Explicit &#xff1b; 将下面脚本复制进去并执行 ValidationMode True InteractiveMode im_Batch Dim mdl the current modelget the current …

大数据项目实践:基于hadoop+spark+mongodb+mysql开发医院临床知识库系统

一、前言 从20世纪90年代数字化医院概念提出到至今的20多年时间&#xff0c;数字化医院&#xff08;Digital Hospital)在国内各大医院飞速的普及推广发展&#xff0c;并取得骄人成绩。不但有数字化医院管理信息系统&#xff08;HIS)、影像存档和通信系统&#xff08;PACS)、电子…

两个对象集合根据对象的某个属性进行过滤,返回不同的部分

例如&#xff1a; plants 和houses 两个集合&#xff0c;进行对比过滤&#xff0c;取出plants里和houses不同的部分&#xff0c;并返回。 List<ImprovedSeedPlant> plants improvedSeedPlantMapper.selectList(spWrapper); List<StudsheepSheepHouse> houses h…

简历

技术岗&#xff01; 那么你应该写什么&#xff1f; 简单的介绍项目的规模&#xff0c;有多少技术人员&#xff0c;是互联网项目还是企业软件&#xff0c;这些就可以了。 接下来你需要写关于项目技术的那部分。 项目用了什么框架&#xff1f;spring 还是struts2 ,持久化是用的是…

java drawimage()方法

转载自 java drawimage()方法图像使用的支持分布于java.applet, java.awt, 和 java.awt.image包中。每一个图像都用一个 java.awt.Image 对象表示。除了Image 类外&#xff0c;java.awt 包提供了其它的基本的图像支持&#xff0c;例如Graphics 类的 drawImage方法&#xff0c;…

老师不能把你怎样,但外面的世界可以!

来源 | 菁优网 本文是一位老师讲述的三段小故事&#xff0c;告诉各位家长及孩子&#xff1a; 亲爱的孩子&#xff0c;老师是不能把你怎样&#xff0c;但外面的世界可以。 1. 我有一个学生&#xff0c;喜欢钻研奥数&#xff0c;却走路慢慢吞吞总爱迟到&#xff0c;同学给他起了…

TagHelper是怎么实现的

众所周知&#xff0c;在asp.net core中编写Razor视图的时候&#xff0c;用了一种新的写法--TagHelper 那这个TagHelper是怎么回事呢? 首先来看看TagHelper的项目位置&#xff0c;它是位于Microsoft.AspNetCore.Mvc.TagHelpers。 如果看到project.json&#xff0c;可以发现&…

java电商面试介绍

写博客 前两个项目采用的是ssh框架搭建的&#xff0c;最近的项目采用的是ssm框架搭建的。在实际开发中&#xff0c;我觉得这两个框架&#xff0c;他们最大的区别在于hibernate与mybatis的区别。 Hibernate与mybatis相比较&#xff0c;mybatis更为轻便、灵活&#xff0c;容易…

java 程序实现对图片的压缩生成缩略图并可设定长宽、尺寸压缩率、图片质量

转载自 java 程序实现对图片的压缩生成缩略图并可设定长宽、尺寸压缩率、图片质量 之前是在另一位高手的上传内容中学习到的&#xff0c;并将其代码根据我的需求进行了修改&#xff0c;参考位置&#xff1a;http://jiangpin1987.javaeye.com/blog/749690参考代码&#xff1a;…

List<Map<String,Object>>集合按照map中的某个值进行排序

List<Map<String,Object>>集合按照map中的某个值进行排序 举例&#xff1a; public void test5(){List<Map<String, Object>> list new ArrayList<Map<String, Object>>();Map<String, Object> map1 new HashMap<String, Obj…

面试

Java简历与面试 置顶2017年10月20日 10:04:29 阅读数&#xff1a;40142 Java就业指导 想要成为合格的Java程序员或工程师到底需要具备哪些专业技能&#xff0c;面试者在面试之前到底需要准备哪些东西呢&#xff1f;本文陈列的这些内容既可以作为个人简历中的内容&#xff0c…