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运行文件拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Maven忽略单元测试及打包到Nexus的实现

    Maven忽略单元测试及打包到Nexus的实现

    我们的工程在打包发布时候,通常都需要忽略单元测试,以免因环境原因,无法通过单元测试而影响发布,本文主要介绍了Maven忽略单元测试及打包到Nexus的实现,感兴趣的可以了解一下
    2024-04-04
  • Java描述数据结构学习之链表的增删改查详解

    Java描述数据结构学习之链表的增删改查详解

    这篇文章主要给大家介绍了关于Java描述数据结构学习之链表的增删改查的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-05-05
  • 浅析Spring IOC bean为什么默认是单例

    浅析Spring IOC bean为什么默认是单例

    单例的意思就是说在 Spring IoC 容器中只会存在一个 bean 的实例,无论一次调用还是多次调用,始终指向的都是同一个 bean 对象,本文小编将和大家一起分析Spring IOC bean为什么默认是单例,需要的朋友可以参考下
    2023-12-12
  • Elasticsearch中FST与前缀搜索应用实战解析

    Elasticsearch中FST与前缀搜索应用实战解析

    这篇文章主要为大家介绍了Elasticsearch中FST与前缀搜索应用实战解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Java利用MD5加盐实现对密码进行加密处理

    Java利用MD5加盐实现对密码进行加密处理

    在开发的时候,有一些敏感信息是不能直接通过明白直接保存到数据库的。最经典的就是密码了。如果直接把密码以明文的形式入库,不仅会泄露用户的隐私,对系统也是极其的不厉。本文就来和大家介绍一下如何对密码进行加密处理,感兴趣的可以了解一下
    2023-02-02
  • spring中的懒加载详细解读

    spring中的懒加载详细解读

    这篇文章主要介绍了spring中的懒加载详细解读,如果某个Bean再程序运行周期中都可能不会被适用,那么可以设定该Bean为懒加载,优势是尽量节省了服务器的资源,缺点是可能会导致某个相应的时间增加,需要的朋友可以参考下
    2023-10-10
  • SpringMVC中的ConversionServiceExposingInterceptor工具类解析

    SpringMVC中的ConversionServiceExposingInterceptor工具类解析

    这篇文章主要介绍了SpringMVC中的ConversionServiceExposingInterceptor工具类解析,ConversionServiceExposingInterceptor是Spring MVC的一个HandlerInterceptor,用于向请求添加一个属性,需要的朋友可以参考下
    2023-12-12
  • Java中的List和MySQL中的varchar相互转换的解决方案

    Java中的List和MySQL中的varchar相互转换的解决方案

    实体类中有一个 List<String> 类型的属性,对应于 MySQL 表里的 varchar 字段,使用 MyBatis 添加或查询时能互相转换,本文给大家介绍Java中的List和MySQL中的varchar相互转换的解决方案,需要的朋友可以参考下
    2024-06-06
  • java获取机器码简单实现demo

    java获取机器码简单实现demo

    这篇文章主要为大家介绍了java获取机器码的简单实现demo,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Java通过apache poi生成excel实例代码

    Java通过apache poi生成excel实例代码

    本篇文章主要介绍了Java通过apache poi生成excel实例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06

最新评论