通过java向jar写入新文件

文章目录

原始需求

有网友提问: 我想在程序中动态地向同一个jar包中添加文件,比如,我的可执行jar包是test.jar,我要在它运行时生成一些xml文件并将这些文件添加到test.jar中,请问如何实现?

分析

test.jar在运行过程中是无法改变自身内容的,但是可以创建内容与test.jar一致的test2.jar

问题就转换成了:

  1. 如何复制已有的test.jar重命名为test2.jar
  2. 如何继续向test2.jar添加新的文件

实施步骤

引入依赖

xml 复制代码
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.5</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-compress</artifactId>
			<version>1.23.0</version>
		</dependency>

核心编码

借助 commons-compress 来操作Jar

java 复制代码
    public void test()
        throws IOException
    {
        String src = getClass().getResource("/apache-jstl.jar").getPath();
        String add1 = getClass().getResource("/servlet-api.jar").getPath();
        String add2 = getClass().getResource("/log4j2.xml").getPath();
        String newJar = src.replace(".jar", DateFormatUtils.format(System.currentTimeMillis(), "_HHmmssSSS") + ".jar");
        log.info("源文件: {}", src);
        log.info("++新增: {}", add1);
        log.info("++新增: {}", add2);
        log.info("新文件: {}", newJar);
        
        try (ArchiveOutputStream outputStream = new JarArchiveOutputStream(new FileOutputStream(newJar));
            JarArchiveInputStream jarInput = new JarArchiveInputStream(new FileInputStream(src)))
        {
            JarArchiveEntry jarEntry;
            while ((jarEntry = jarInput.getNextJarEntry()) != null)
            {
                if (!jarEntry.isDirectory())
                {
                    outputStream.putArchiveEntry(jarEntry);
                    IOUtils.copy(jarInput, outputStream);
                }
            }
            outputStream.flush();
            
            // 追加addFiles
            File[] addFiles = {new File(add1), new File(add2)};
            for (File addFile : addFiles)
            {
                JarArchiveEntry addEntry = new JarArchiveEntry("add/" + addFile.getName());
                outputStream.putArchiveEntry(addEntry);
                try (InputStream entryInputStream = new FileInputStream(addFile))
                {
                    IOUtils.copy(entryInputStream, outputStream);
                }
            }
            
            // 追加add/001.txt
            JarArchiveEntry entry = new JarArchiveEntry("add/001.txt");
            outputStream.putArchiveEntry(entry);
            outputStream.write("org.apache.commons.compress.archivers.jar.JarArchiveOutputStream;".getBytes(StandardCharsets.UTF_8));
            outputStream.closeArchiveEntry();
            outputStream.finish();
        }
    }

使用JDK API实现

java 复制代码
    public void test2()
    {
        try
        {
            String src = getClass().getResource("/apache-jstl.jar").getPath();
            String add1 = getClass().getResource("/servlet-api.jar").getPath();
            String add2 = getClass().getResource("/log4j2.xml").getPath();
            String newJar = src.replace(".jar", DateFormatUtils.format(System.currentTimeMillis(), "_HHmmssSSS") + ".jar");
            log.info("源文件: {}", src);
            log.info("++新增: {}", add1);
            log.info("++新增: {}", add2);
            log.info("新文件: {}", newJar);
            addFilesToJar(new File(src), newJar, new File(add1), new File(add2));
        }
        catch (IOException e)
        {
            log.error(e.getMessage(), e);
        }
    }
    
    /**
     * JDK-API实现-将addFiles添加到srcJar并重命名为newJar
     * 
     * @param srcJar
     * @param newJar
     * @param addFiles
     * @throws IOException
     */
    private void addFilesToJar(File srcJar, String newJar, File... addFiles)
        throws IOException
    {
        try (JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(newJar)); JarFile jarFile = new JarFile(srcJar))
        {
            // 遍历jar文件数据写入新jar
            Enumeration<JarEntry> entrys = jarFile.entries();
            while (entrys.hasMoreElements())
            {
                JarEntry jarEntry = entrys.nextElement();
                if (!jarEntry.isDirectory())
                {
                    jarOutputStream.putNextEntry(jarEntry);
                    try (InputStream entryInputStream = jarFile.getInputStream(jarEntry))
                    {
                        IOUtils.copy(entryInputStream, jarOutputStream);
                    }
                }
            }
            
            // 追加写入
            for (File addFile : addFiles)
            {
                JarEntry jarEntry = new JarEntry("add/" + addFile.getName());
                jarOutputStream.putNextEntry(jarEntry);
                try (InputStream entryInputStream = new FileInputStream(addFile))
                {
                    IOUtils.copy(entryInputStream, jarOutputStream);
                }
            }
        }
    }

运行效果

原始文件

运行后:


大功告成!!!

有任何问题和建议,都可以向我提问讨论,大家一起进步,谢谢!

-over-

相关推荐
:Concerto21 分钟前
JavaSE 注解
java·开发语言·sprint
电商API_180079052471 小时前
第三方淘宝商品详情 API 全维度调用指南:从技术对接到生产落地
java·大数据·前端·数据库·人工智能·网络爬虫
一点程序1 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
C雨后彩虹1 小时前
计算疫情扩散时间
java·数据结构·算法·华为·面试
2601_949809591 小时前
flutter_for_openharmony家庭相册app实战+我的Tab实现
java·javascript·flutter
vx_BS813302 小时前
【直接可用源码免费送】计算机毕业设计精选项目03574基于Python的网上商城管理系统设计与实现:Java/PHP/Python/C#小程序、单片机、成品+文档源码支持定制
java·python·课程设计
2601_949868362 小时前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
达文汐2 小时前
【困难】力扣算法题解析LeetCode332:重新安排行程
java·数据结构·经验分享·算法·leetcode·力扣
培风图南以星河揽胜2 小时前
Java版LeetCode热题100之零钱兑换:动态规划经典问题深度解析
java·leetcode·动态规划
启山智软3 小时前
【中大企业选择源码部署商城系统】
java·spring·商城开发