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运行文件拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
SpringMVC中的ConversionServiceExposingInterceptor工具类解析
这篇文章主要介绍了SpringMVC中的ConversionServiceExposingInterceptor工具类解析,ConversionServiceExposingInterceptor是Spring MVC的一个HandlerInterceptor,用于向请求添加一个属性,需要的朋友可以参考下2023-12-12Java中的List和MySQL中的varchar相互转换的解决方案
实体类中有一个 List<String> 类型的属性,对应于 MySQL 表里的 varchar 字段,使用 MyBatis 添加或查询时能互相转换,本文给大家介绍Java中的List和MySQL中的varchar相互转换的解决方案,需要的朋友可以参考下2024-06-06
最新评论