springboot运行jar包,实现复制jar包resources下文件、文件夹(可支持包含子文件夹)到指定的目录

背景

以jar包运行时,获取文件目录时,会报错;

idea运行不会报错。

代码:

//复制文件夹到指定路径下
String srcFilePath = Thread.currentThread().getContextClassLoader().getResource("").getPath()
						+ "/templates";
FileUtils.copyFolder(new File(srcFilePath), dir);

    /**
     * 文件夹的复制
     * @param srcFile 源文件夹File对象
     * @param destFile 目标文件夹File对象
     * @throws IOException IOException
     */
    public static void copyFolder(File srcFile, File destFile) throws IOException {
        //判断数据源File是否是文件
        if (srcFile.isDirectory()) {
            //在目的地下创建和数据源File名称一样的目录
            String srcFileName = srcFile.getName();
            File newFolder = new File(destFile, srcFileName);
            if (!newFolder.exists()) {
                newFolder.mkdir();
            }
            //获取数据源File下所有文件或者目录的File数组
            File[] listFiles = srcFile.listFiles();

            //遍历该File数组,得到每一个File对象
            for (File file : listFiles) {
                //把该File作为数据源File对象,递归调用复制文件夹的方法
                copyFolder(file, newFolder);
            }
        } else {
            //说明是文件,直接用字节流复制
            File newFile = new File(destFile, srcFile.getName());
            copyFile(srcFile, newFile);
        }

    }

    /**
     * 复制文件
     * @param srcFile 源文件
     * @param destFile 目标文件
     * @throws IOException IOException
     */
    public static void copyFile(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            if (!destFile.getParentFile().exists()) {
                destFile.getParentFile().mkdirs();
            }

            if (!destFile.exists()) {
                destFile.createNewFile();
            }

            bis = new BufferedInputStream(new FileInputStream(srcFile));
            bos = new BufferedOutputStream(new FileOutputStream(destFile));

            int len;
            byte[] bys = new byte[1024];
            while ((len = bis.read(bys)) != -1) {
                bos.write(bys, 0, len);
            }
        } finally {

            if (null != bos) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != bis) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

这行:bis = new BufferedInputStream(new FileInputStream(srcFile));

文件复制时,报错:

java.io.FileNotFoundException: file:/xxx/xxx.jar!/BOOT-INF/classes!/xxx/xxx (No such file or directory)

或 

java.io.FileNotFoundException: file:\xxx\xxx.jar!\BOOT-INF\classes!\xxx\xxx (文件名、目录名或卷标语法不正确。)

问题产生原因:当我们使用文件路径访问文件时,该路径下的文件必须是可访问的,而jar文件本质是上是一个压缩文件,需要解压才能访问,所以程序会直接报错。

解决方案

更改复制文件夹方式,不读文件路径,直接读取文件流。

以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

这边使用resource.getDescription()进行获取文件路径,然后进行格式化处理。

FreeMarkerUtil工具类

import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ClassUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 复制resource文件、文件夹
 */
public class FreeMarkerUtil {

    /**
     * 复制path目录下所有文件,覆盖(jar)
     *
     * @param path    文件目录 不能以/开头
     * @param newPath 新文件目录
     */
    public static void copyFolderFromJarCover(String path, String newPath) throws IOException {
        if (!new File(newPath).exists()) {
            new File(newPath).mkdir();
        }
        if (path.contains("\\")) {
            path = path.replace("\\", "/");
        }
        if (path.startsWith("/")) {
            //以/开头,去掉/
            path = path.substring(1);
        }
        if (path.endsWith("/")) {
            //以/结尾,去掉/
            path = path.substring(0, path.length() - 1);
        }

        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        //获取所有匹配的文件(包含根目录文件、子目录、子目录下的文件)
        Resource[] resources = resolver.getResources("classpath:" + path + "/**/");
        //打印有多少文件
        for (Resource resource : resources) {
            //文件名
            String filename = resource.getFilename();

            //以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("\\", "/");
            //保留 /xxx/xxx
            description = description.replaceAll("(.*\\[)|(]$)", "").trim();

            //以“文件目录”进行分割,获取文件相对路径
            String[] descriptions = description.split(path + "/");
            if (descriptions.length > 1) {
                //获取文件相对路径,/xxx/xxx
                String relativePath = descriptions[1];

                //新文件路径
                String newFilePath = newPath + "/" + relativePath;
                if (FreeMarkerUtil.isDirectory(filename)) {
                    //文件夹
                    if (!new File(newFilePath).exists()) {
                        new File(newFilePath).mkdir();
                    }

                } else {

                    //文件
                    InputStream stream = resource.getInputStream();
                    write2File(stream, newFilePath);
                }
            }
        }
    }

    /**
     * 复制path目录下所有文件,不覆盖(jar)
     *
     * @param path    文件目录 不能以/开头
     * @param newPath 新文件目录
     */
    public static void copyFolderFromJar(String path, String newPath) throws IOException {
        if (!new File(newPath).exists()) {
            new File(newPath).mkdir();
        }
        if (path.contains("\\")) {
            path = path.replace("\\", "/");
        }
        if (path.startsWith("/")) {
            //以/开头,去掉/
            path = path.substring(1);
        }
        if (path.endsWith("/")) {
            //以/结尾,去掉/
            path = path.substring(0, path.length() - 1);
        }

        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        //获取所有匹配的文件(包含根目录文件、子目录、子目录下的文件)
        Resource[] resources = resolver.getResources("classpath:" + path + "/**/");
        //打印有多少文件
        for (Resource resource : resources) {
            //文件名
            String filename = resource.getFilename();

            //以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("\\", "/");
            //保留 /xxx/xxx
            description = description.replaceAll("(.*\\[)|(]$)", "").trim();

            //以“文件目录”进行分割,获取文件相对路径
            String[] descriptions = description.split(path + "/");
            if (descriptions.length > 1) {
                //获取文件相对路径,/xxx/xxx
                String relativePath = descriptions[1];

                //新文件路径
                String newFilePath = newPath + "/" + relativePath;
                if (FreeMarkerUtil.isDirectory(filename)) {
                    //文件夹
                    if (!new File(newFilePath).exists()) {
                        new File(newFilePath).mkdir();
                    }

                } else {

                    //文件
                    File f = new File(newFilePath);
                    if (!f.exists()) {
                        InputStream stream = resource.getInputStream();
                        write2File(stream, newFilePath);
                    }
                }
            }
        }
    }

    /**
     * 复制文件(jar)
     *
     * @param path    源文件路径
     * @param newPath 新文件路径
     */
    public static void copyFileFromJar(String path, String newPath) throws IOException {
        //不读文件路径,直接读取文件流
        InputStream inputStream = ClassUtils
                .getDefaultClassLoader()
                .getResourceAsStream(path);
        write2File(inputStream, newPath);
    }

    /**
     * 输入流写入文件
     *
     * @param is       输入流
     * @param filePath 文件保存目录路径
     * @throws IOException IOException
     */
    public static void write2File(InputStream is, String filePath) throws IOException {
        File destFile = new File(filePath);
        if (!destFile.getParentFile().exists()) {
            destFile.getParentFile().mkdirs();
        }

        if (!destFile.exists()) {
            destFile.createNewFile();
        }

        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();
    }

    /**
     * 判断是否为目录、文件夹
     * @param filename 文件名
     * @return boolean
     */
    public static boolean isDirectory(String filename) {
        if (StringUtils.isBlank(filename)) {
            return false;
        }

        //有后缀,是文件
        boolean contains = filename.contains(".");
        if (contains) {
            return false;
        }

        //是文件夹
        return true;
    }

    /**
     * 判断是否为目录、文件夹
     * @param path 文件路径
     * @return boolean
     */
    public static boolean isDirectoryFromPath(String path) {
        if (StringUtils.isBlank(path)) {
            return false;
        }

        String newPath = path.replace("\\", "/");
        if (newPath.contains("/")) {
            newPath = newPath.substring(newPath.lastIndexOf("/"));
        }

        //有后缀,是文件;
        boolean contains = newPath.contains(".");
        if (contains) {
            return false;
        }

        //是文件夹
        return true;
    }

    public static void main(String[] args) throws IOException {
        //文件夹复制
        String path = "templates";
        String newPath = "D:/tmp";
        FreeMarkerUtil.copyFolderFromJarCover(path, newPath);

        //文件复制
        String filePath = "application.properties";
        String newFilePath = "D:/tmp/application.properties";
        FreeMarkerUtil.copyFileFromJar(filePath, newFilePath);
    }

}

文件复制

同样道理,以jar包运行也会报错。

解决方案:

//文件复制
String filePath = "application.properties";
String newFilePath = "D:/tmp/application.properties";
FreeMarkerUtil.copyFileFromJar(filePath, newFilePath);

    /**
     * 复制文件(jar)
     *
     * @param path    源文件路径
     * @param newPath 新文件路径
     */
    public static void copyFileFromJar(String path, String newPath) throws IOException {
        //不读文件路径,直接读取文件流
        InputStream inputStream = ClassUtils
                .getDefaultClassLoader()
                .getResourceAsStream(path);
        write2File(inputStream, newPath);
    }

你可能感兴趣的:(java,spring,boot,jar,后端)