背景
因为执行需要,需要把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中的文件,拷贝到指定的路径下,记录备查