当前位置: 首页 > news >正文

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;@PostConstructpublic 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/xxxdescription = 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");}@PostConstructpublic 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/xxxdescription = 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中的文件,拷贝到指定的路径下,记录备查

相关文章:

  • hot100经典:困难 Leetcode 4. 寻找两个正序数组的中位数
  • C++ 20新特性之三向比较运算符
  • UG数控编程入门:从基础到精通的全方位指南
  • 一个 python+tensorFlow训练1万张图片分类的简单直观例子( 回答由百度 AI 给出 )
  • 呆滞物料规范管理了,问题就好办了
  • 循环嵌套语句的实际应用(2)
  • 标准价与移动平均价简介
  • 让 AI 写高考作文丨10 款大模型 “交卷”,实力水平如何?
  • Nginx配置负载均衡
  • 近期面试HW中级蓝问题(非常详细)零基础入门到精通,收藏这一篇就够了
  • 计算机组成原理(一)
  • Mac电脑重置网络命令
  • Vue3【十】07使用ref创建基本类型的响应式数据以及ref和reactive区别
  • FM1202,FM020和利时备品
  • Docker的资源限制
  • C++11: atomic 头文件
  • CSS3 变换
  • Flex布局到底解决了什么问题
  • JavaScript函数式编程(一)
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • React Native移动开发实战-3-实现页面间的数据传递
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • SSH 免密登录
  • VUE es6技巧写法(持续更新中~~~)
  • vue的全局变量和全局拦截请求器
  • 彻底搞懂浏览器Event-loop
  • 爬虫模拟登陆 SegmentFault
  • 实现菜单下拉伸展折叠效果demo
  • 算法系列——算法入门之递归分而治之思想的实现
  • kubernetes资源对象--ingress
  • ​【已解决】npm install​卡主不动的情况
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • (13)DroneCAN 适配器节点(一)
  • (2)(2.10) LTM telemetry
  • (2)nginx 安装、启停
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (el-Transfer)操作(不使用 ts):Element-plus 中 Select 组件动态设置 options 值需求的解决过程
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (学习日记)2024.03.25:UCOSIII第二十二节:系统启动流程详解
  • (已解决)什么是vue导航守卫
  • (轉)JSON.stringify 语法实例讲解
  • *p++,*(p++),*++p,(*p)++区别?
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .ai域名是什么后缀?
  • .apk 成为历史!
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .htaccess 强制https 单独排除某个目录
  • .Net 6.0 Windows平台如何判断当前电脑是否联网
  • .NET CORE 3.1 集成JWT鉴权和授权2
  • .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions