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/xxxdescription = description.replaceAll("(.*\\[)|(]$)", "").trim();//以“文件目录”进行分割,获取文件相对路径String[] descriptions = description.split(path + "/");if (descriptions.length > 1) {//获取文件相对路径,/xxx/xxxString 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/xxxdescription = description.replaceAll("(.*\\[)|(]$)", "").trim();//以“文件目录”进行分割,获取文件相对路径String[] descriptions = description.split(path + "/");if (descriptions.length > 1) {//获取文件相对路径,/xxx/xxxString 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);}