java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Springboot jar运行文件拷贝

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

作者:涟漪海洋

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

您可能感兴趣的文章:
阅读全文