Springboot jar运行时如何将jar内的文件拷贝到文件系统中

 更新时间:2024年06月05日 15:51:28   作者:涟漪海洋  
因为执行需要,需要把jar内templates文件夹下的的文件夹及文件加压到宿主机器的某个路径下,以便执行对应的脚本文件,这篇文章主要介绍了Springboot jar运行时如何将jar内的文件拷贝到文件系统中,需要的朋友可以参考下

背景

因为执行需要,需要把jar内templates文件夹下的的文件夹及文件加压到宿主机器的某个路径下, 以便执行对应的脚本文件

PS: 通过类加载器等方式,直接getFile遍历文件,在idea中运行是没问题的,但是当打包成jar运行就会出现问题,因为jar内文件的路径不是真实路径,会出现异常

java.io.FileNotFoundException: class path resource [xxx/xxx/] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:xxx.jar!/BOOT-INF/classes!/xxx/xxx

方式一 

知道文件名的情况下,无需一层一层的遍历,将文件路径都指定好,然后根据流文件拷贝

package com.aimsphm.practice;
import lombok.extern.slf4j.Slf4j;
import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.util.ObjectUtils;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
@Component
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
    @Value("${customer.config.data-root:/usr/data/}")
    private String dataRoot;
    @PostConstruct
    public void initDatabase() {
        dataRoot = dataRoot.endsWith("/") ? dataRoot : dataRoot + "/";
        List<String> fileList = getFiles();
        fileList.stream().filter(x -> !ObjectUtils.isEmpty(x)).forEach(x -> {
            try {
                URL resource = App.class.getClassLoader().getResource(x);
                InputStream inputStream = resource.openStream();
                if (ObjectUtils.isEmpty(inputStream)) {
                    return;
                }
                File file = new File(dataRoot + x);
                if (!file.exists()) {
                    FileUtils.copyInputStreamToFile(inputStream, file);
                }
            } catch (IOException e) {
                log.error("失败:",e)
            }
        });
    }
    private List<String> getFiles() {
        return Lists.newArrayList(
                "db/practice.db",
                "local-data/0/p-1.jpg",
                "local-data/0/p-2.jpg",
                "local-data/0/p-3.jpg",
                "local-data/0/p-4.jpg",
                "local-data/1/meter-1.png",
                "local-data/-1/yw-1.png",
                "local-data/sound/test.txt",
                "local-data/multi/1/meter-multi.jpg",
                "local-data/multi/-1/yewei.png",
                "");
    }
}

方式二

通过resource的方式,获取文件的描述信息,然后根据描述信息,获取文件的路径信息,然后通过拷贝流文件,将文件最终拷贝到指定的路径下

package com.example.demo.test;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
 * 复制resource文件、文件夹
 *
 * @author MILLA
 */
@Component
@Slf4j
public class JarFileUtil {
    public void copyFolderFromJar() throws IOException {
        this.copyFolderFromJar("templates", "/usr/data/files");
    }
    /**
     * 复制path目录下所有文件到指定的文件系统中
     *
     * @param path    文件目录 不能以/开头
     * @param newPath 新文件目录
     */
    public void copyFolderFromJar(String path, String newPath) throws IOException {
        path = preOperation(path, newPath);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        //获取所有匹配的文件(包含根目录文件、子目录、子目录下的文件)
        Resource[] resources = resolver.getResources("classpath:" + path + "/**");
        //打印有多少文件
        for (Resource resource : resources) {
            //文件名
            //以jar包运行时,不能使用resource.getFile()获取文件路径、判断是否为文件等,会报错:
            //java.io.FileNotFoundException: class path resource [xxx/xxx/] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:xxx.jar!/BOOT-INF/classes!/xxx/xxx
            //文件路径
            //file [/xxx/xxx]
            String description = resource.getDescription();
            description = description.replace("\\", "/");
            description = description.replace(path, "/");
            //保留 /xxx/xxx
            description = description.replaceAll("(.*\\[)|(]$)", "").trim();
            //以“文件目录”进行分割,获取文件相对路径
            //获取文件相对路径,/xxx/xxx
            //新文件路径
            String newFilePath = newPath + "/" + description;
            if (newFilePath.endsWith("/")) {
                File file = new File(newFilePath);
                //文件夹
                if (file.exists()) {
                    boolean mkdir = file.mkdir();
                    log.debug("路径[{}]创建是否成功状态:{} ", newFilePath, mkdir);
                }
            } else {
                //文件
                InputStream stream = resource.getInputStream();
                write2File(stream, newFilePath);
            }
        }
    }
    /**
     * 文件预处理
     *
     * @param path    原文件路径
     * @param newPath 目标路径
     * @return 新的路径字符串
     */
    private static String preOperation(String path, String newPath) {
        if (!new File(newPath).exists()) {
            boolean mkdir = new File(newPath).mkdir();
            log.debug("路径[{}]创建是否成功状态:{} ", newPath, mkdir);
        }
        if (path.contains("\\")) {
            path = path.replace("\\", "/");
        }
        //保证没有重复的/出现
        path = path.replaceAll("(?<!\\G/|[^/])/+", "");
        if (path.startsWith("/")) {
            //以/开头,去掉/
            path = path.substring(1);
        }
        if (path.endsWith("/")) {
            //以/结尾,去掉/
            path = path.substring(0, path.length() - 1);
        }
        return path;
    }
    /**
     * 输入流写入文件
     *
     * @param is       输入流
     * @param filePath 文件保存目录路径
     * @throws IOException IOException
     */
    public static void write2File(InputStream is, String filePath) throws IOException {
        File destFile = new File(filePath);
        File parentFile = destFile.getParentFile();
        boolean mkdirs = parentFile.mkdirs();
        log.debug("路径[{}]创建是否成功状态:{} ", filePath, mkdirs);
        if (!destFile.exists()) {
            boolean newFile = destFile.createNewFile();
            log.debug("路径[{}]创建是否成功状态:{} ", destFile.getPath(), newFile);
        }
        OutputStream os = new FileOutputStream(destFile);
        int len = 8192;
        byte[] buffer = new byte[len];
        while ((len = is.read(buffer, 0, len)) != -1) {
            os.write(buffer, 0, len);
        }
        os.close();
        is.close();
    }
    public static void main(String[] args) throws IOException {
        //文件夹复制
        String path = "templates";
        String newPath = "D:/tmp";
        new JarFileUtil().copyFolderFromJar(path, newPath);
    }
}

 如果开发中使用一些文件操作依赖,可简化代码如下

<!--文件依赖 --> 
<dependency>
     <groupId>commons-fileupload</groupId>
     <artifactId>commons-fileupload</artifactId>
     <version>1.3.3</version>
 </dependency>
package com.example.demo.test;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.File;
/**
 * <p>
 * 功能描述:
 * </p>
 *
 * @author MILLA
 * @version 1.0
 * @since 2024/05/31 16:30
 */
@Slf4j
@Component
public class JarFileUtil{
    public static void main(String[] args) {
        JarFileUtilinit = new JarFileUtil();
        init.copyFile2Temp("//templates//shell//", "/usr/data/shell/files");
    }
    @PostConstruct
    public void copyFile2Temp() {
    }
    public void copyFile2Temp(String source, String target) {
        try {
            source = preOperation(source, target);
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources(source + "/**");
            for (int i = 0; i < resources.length; i++) {
                Resource resource = resources[i];
//                resource.getFile() jar运行时候不能用该方法获取文件,因为jar的路径不对
                String description = resource.getDescription();
                description = description.replace("\\", "/");
                description = description.replace(source, "/");
                //保留 /xxx/xxx
                description = description.replaceAll("(.*\\[)|(]$)", "").trim();
                //以“文件目录”进行分割,获取文件相对路径
                //获取文件相对路径,/xxx/xxx
                //新文件路径
                String newFilePath = target + File.separator + description;
                File file = new File(newFilePath);
                if (newFilePath.endsWith("/")) {
                    boolean mkdirs = file.mkdirs();
                    log.debug("路径[{}]创建是否成功状态:{} ", newFilePath, mkdirs);
                } else {
                    FileUtils.copyInputStreamToFile(resource.getInputStream(), file);
                }
            }
        } catch (Exception e) {
            log.error("文件拷贝异常:", e);
        }
    }
    private static String preOperation(String source, String target) {
        if (!new File(target).exists()) {
            boolean mkdir = new File(target).mkdir();
            log.debug("路径[{}]创建是否成功状态:{} ", target, mkdir);
        }
        if (source.contains("\\")) {
            source = source.replace("\\", "/");
        }
        //保证没有重复的/出现
        source = source.replaceAll("(?<!\\G/|[^/])/+", "");
        if (source.startsWith("/")) {
            //以/开头,去掉/
            source = source.substring(1);
        }
        if (source.endsWith("/")) {
            //以/结尾,去掉/
            source = source.substring(0, source.length() - 1);
        }
        return source;
    }
}

 通过这种方式,就能将正在运行的jar中的文件,拷贝到指定的路径下,记录备查

到此这篇关于Springboot jar运行时如何将jar内的文件拷贝到文件系统中的文章就介绍到这了,更多相关Springboot jar运行文件拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Springboot集成ClickHouse及应用场景分析

    Springboot集成ClickHouse及应用场景分析

    这篇文章主要介绍了Springboot集成ClickHouse的实例代码,本文通过应用场景实例代码介绍了整合springboot的详细过程,感兴趣的朋友跟随小编一起看看吧
    2022-02-02
  • spring boot+thymeleaf+bootstrap实现后台管理系统界面

    spring boot+thymeleaf+bootstrap实现后台管理系统界面

    这篇文章主要为大家详细介绍了spring boot+thymeleaf+bootstrap简单实现后台管理系统界面,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • SpringBoot加载读取配置文件过程详细分析

    SpringBoot加载读取配置文件过程详细分析

    在实际的项目开发过程中,我们经常需要将某些变量从代码里面抽离出来,放在配置文件里面,以便更加统一、灵活的管理服务配置信息。所以本文将为大家总结一下SpringBoot加载配置文件的常用方式,需要的可以参考一下
    2023-01-01
  • MyBatis OGNL 表达式的避坑指南

    MyBatis OGNL 表达式的避坑指南

    本文主要介绍了MyBatis OGNL 表达式的避坑指南,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-09-09
  • 解决mybatis 中collection嵌套collection引发的bug

    解决mybatis 中collection嵌套collection引发的bug

    这篇文章主要介绍了解决mybatis 中collection嵌套collection引发的bug,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • SpringBoot+Vue+Element-ui实现前后端分离

    SpringBoot+Vue+Element-ui实现前后端分离

    使用前后端分离的方式,可以减少代码耦合,本文主要介绍了SpringBoot+Vue+Element-ui实现前后端分离,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • idea 安装 Mybatis 开发帮助插件 MyBatisCodeHelper-Pro 插件破解版的方法

    idea 安装 Mybatis 开发帮助插件 MyBatisCodeHelper-Pro 插件破解版的方法

    MyBatisCodeHelper-Pro 插件可以帮助我们快速的开发 mybatis,这篇文章给大家介绍idea 安装 Mybatis 开发帮助插件 MyBatisCodeHelper-Pro 插件破解版的相关知识,感兴趣的朋友跟随小编一起看看吧
    2020-09-09
  • java对图片进行压缩和resize缩放的方法

    java对图片进行压缩和resize缩放的方法

    本篇文章主要介绍了java对图片进行压缩和resize调整的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • java开发主流定时任务解决方案全横评详解

    java开发主流定时任务解决方案全横评详解

    这篇文章主要为大家介绍了java开发主流定时任务解决方案全横评详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • JAVA注解代码详解一篇就够了

    JAVA注解代码详解一篇就够了

    这篇文章主要介绍了Java注解详细介绍,本文讲解了Java注解是什么、Java注解基础知识、Java注解类型、定义Java注解类型的注意事项等内容,需要的朋友可以参考下
    2021-07-07

最新评论