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

作者 : admin 本文共6934个字,预计阅读时间需要18分钟 发布时间: 2024-06-9 共2人阅读

背景

因为执行需要,需要把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 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 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",
                "");
    }
}

Springboot jar运行时,将jar内的文件拷贝到文件系统中插图

方式二

通过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);

    }

}

Springboot jar运行时,将jar内的文件拷贝到文件系统中插图(1)

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

 

     commons-fileupload
     commons-fileupload
     1.3.3
 

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;

/**
 * 

* 功能描述: *

* * @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内的文件拷贝到文件系统中
E-->