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


    背景

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

    idea运行不会报错。

    代码:

    1. //复制文件夹到指定路径下
    2. String srcFilePath = Thread.currentThread().getContextClassLoader().getResource("").getPath()
    3. + "/templates";
    4. FileUtils.copyFolder(new File(srcFilePath), dir);
    1. /**
    2. * 文件夹的复制
    3. * @param srcFile 源文件夹File对象
    4. * @param destFile 目标文件夹File对象
    5. * @throws IOException IOException
    6. */
    7. public static void copyFolder(File srcFile, File destFile) throws IOException {
    8. //判断数据源File是否是文件
    9. if (srcFile.isDirectory()) {
    10. //在目的地下创建和数据源File名称一样的目录
    11. String srcFileName = srcFile.getName();
    12. File newFolder = new File(destFile, srcFileName);
    13. if (!newFolder.exists()) {
    14. newFolder.mkdir();
    15. }
    16. //获取数据源File下所有文件或者目录的File数组
    17. File[] listFiles = srcFile.listFiles();
    18. //遍历该File数组,得到每一个File对象
    19. for (File file : listFiles) {
    20. //把该File作为数据源File对象,递归调用复制文件夹的方法
    21. copyFolder(file, newFolder);
    22. }
    23. } else {
    24. //说明是文件,直接用字节流复制
    25. File newFile = new File(destFile, srcFile.getName());
    26. copyFile(srcFile, newFile);
    27. }
    28. }
    29. /**
    30. * 复制文件
    31. * @param srcFile 源文件
    32. * @param destFile 目标文件
    33. * @throws IOException IOException
    34. */
    35. public static void copyFile(File srcFile, File destFile) throws IOException {
    36. BufferedInputStream bis = null;
    37. BufferedOutputStream bos = null;
    38. try {
    39. if (!destFile.getParentFile().exists()) {
    40. destFile.getParentFile().mkdirs();
    41. }
    42. if (!destFile.exists()) {
    43. destFile.createNewFile();
    44. }
    45. bis = new BufferedInputStream(new FileInputStream(srcFile));
    46. bos = new BufferedOutputStream(new FileOutputStream(destFile));
    47. int len;
    48. byte[] bys = new byte[1024];
    49. while ((len = bis.read(bys)) != -1) {
    50. bos.write(bys, 0, len);
    51. }
    52. } finally {
    53. if (null != bos) {
    54. try {
    55. bos.close();
    56. } catch (IOException e) {
    57. e.printStackTrace();
    58. }
    59. }
    60. if (null != bis) {
    61. try {
    62. bis.close();
    63. } catch (IOException e) {
    64. e.printStackTrace();
    65. }
    66. }
    67. }
    68. }

    这行: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工具类

    1. import org.apache.commons.lang.StringUtils;
    2. import org.springframework.core.io.Resource;
    3. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    4. import org.springframework.core.io.support.ResourcePatternResolver;
    5. import org.springframework.util.ClassUtils;
    6. import java.io.File;
    7. import java.io.FileOutputStream;
    8. import java.io.IOException;
    9. import java.io.InputStream;
    10. import java.io.OutputStream;
    11. /**
    12. * 复制resource文件、文件夹
    13. */
    14. public class FreeMarkerUtil {
    15. /**
    16. * 复制path目录下所有文件,覆盖(jar)
    17. *
    18. * @param path 文件目录 不能以/开头
    19. * @param newPath 新文件目录
    20. */
    21. public static void copyFolderFromJarCover(String path, String newPath) throws IOException {
    22. if (!new File(newPath).exists()) {
    23. new File(newPath).mkdir();
    24. }
    25. if (path.contains("\\")) {
    26. path = path.replace("\\", "/");
    27. }
    28. if (path.startsWith("/")) {
    29. //以/开头,去掉/
    30. path = path.substring(1);
    31. }
    32. if (path.endsWith("/")) {
    33. //以/结尾,去掉/
    34. path = path.substring(0, path.length() - 1);
    35. }
    36. ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    37. //获取所有匹配的文件(包含根目录文件、子目录、子目录下的文件)
    38. Resource[] resources = resolver.getResources("classpath:" + path + "/**/");
    39. //打印有多少文件
    40. for (Resource resource : resources) {
    41. //文件名
    42. String filename = resource.getFilename();
    43. //以jar包运行时,不能使用resource.getFile()获取文件路径、判断是否为文件等,会报错:
    44. //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
    45. //文件路径
    46. //file [/xxx/xxx]
    47. String description = resource.getDescription();
    48. description = description.replace("\\", "/");
    49. //保留 /xxx/xxx
    50. description = description.replaceAll("(.*\\[)|(]$)", "").trim();
    51. //以“文件目录”进行分割,获取文件相对路径
    52. String[] descriptions = description.split(path + "/");
    53. if (descriptions.length > 1) {
    54. //获取文件相对路径,/xxx/xxx
    55. String relativePath = descriptions[1];
    56. //新文件路径
    57. String newFilePath = newPath + "/" + relativePath;
    58. if (FreeMarkerUtil.isDirectory(filename)) {
    59. //文件夹
    60. if (!new File(newFilePath).exists()) {
    61. new File(newFilePath).mkdir();
    62. }
    63. } else {
    64. //文件
    65. InputStream stream = resource.getInputStream();
    66. write2File(stream, newFilePath);
    67. }
    68. }
    69. }
    70. }
    71. /**
    72. * 复制path目录下所有文件,不覆盖(jar)
    73. *
    74. * @param path 文件目录 不能以/开头
    75. * @param newPath 新文件目录
    76. */
    77. public static void copyFolderFromJar(String path, String newPath) throws IOException {
    78. if (!new File(newPath).exists()) {
    79. new File(newPath).mkdir();
    80. }
    81. if (path.contains("\\")) {
    82. path = path.replace("\\", "/");
    83. }
    84. if (path.startsWith("/")) {
    85. //以/开头,去掉/
    86. path = path.substring(1);
    87. }
    88. if (path.endsWith("/")) {
    89. //以/结尾,去掉/
    90. path = path.substring(0, path.length() - 1);
    91. }
    92. ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    93. //获取所有匹配的文件(包含根目录文件、子目录、子目录下的文件)
    94. Resource[] resources = resolver.getResources("classpath:" + path + "/**/");
    95. //打印有多少文件
    96. for (Resource resource : resources) {
    97. //文件名
    98. String filename = resource.getFilename();
    99. //以jar包运行时,不能使用resource.getFile()获取文件路径、判断是否为文件等,会报错:
    100. //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
    101. //文件路径
    102. //file [/xxx/xxx]
    103. String description = resource.getDescription();
    104. description = description.replace("\\", "/");
    105. //保留 /xxx/xxx
    106. description = description.replaceAll("(.*\\[)|(]$)", "").trim();
    107. //以“文件目录”进行分割,获取文件相对路径
    108. String[] descriptions = description.split(path + "/");
    109. if (descriptions.length > 1) {
    110. //获取文件相对路径,/xxx/xxx
    111. String relativePath = descriptions[1];
    112. //新文件路径
    113. String newFilePath = newPath + "/" + relativePath;
    114. if (FreeMarkerUtil.isDirectory(filename)) {
    115. //文件夹
    116. if (!new File(newFilePath).exists()) {
    117. new File(newFilePath).mkdir();
    118. }
    119. } else {
    120. //文件
    121. File f = new File(newFilePath);
    122. if (!f.exists()) {
    123. InputStream stream = resource.getInputStream();
    124. write2File(stream, newFilePath);
    125. }
    126. }
    127. }
    128. }
    129. }
    130. /**
    131. * 复制文件(jar)
    132. *
    133. * @param path 源文件路径
    134. * @param newPath 新文件路径
    135. */
    136. public static void copyFileFromJar(String path, String newPath) throws IOException {
    137. //不读文件路径,直接读取文件流
    138. InputStream inputStream = ClassUtils
    139. .getDefaultClassLoader()
    140. .getResourceAsStream(path);
    141. write2File(inputStream, newPath);
    142. }
    143. /**
    144. * 输入流写入文件
    145. *
    146. * @param is 输入流
    147. * @param filePath 文件保存目录路径
    148. * @throws IOException IOException
    149. */
    150. public static void write2File(InputStream is, String filePath) throws IOException {
    151. File destFile = new File(filePath);
    152. if (!destFile.getParentFile().exists()) {
    153. destFile.getParentFile().mkdirs();
    154. }
    155. if (!destFile.exists()) {
    156. destFile.createNewFile();
    157. }
    158. OutputStream os = new FileOutputStream(destFile);
    159. int len = 8192;
    160. byte[] buffer = new byte[len];
    161. while ((len = is.read(buffer, 0, len)) != -1) {
    162. os.write(buffer, 0, len);
    163. }
    164. os.close();
    165. is.close();
    166. }
    167. /**
    168. * 判断是否为目录、文件夹
    169. * @param filename 文件名
    170. * @return boolean
    171. */
    172. public static boolean isDirectory(String filename) {
    173. if (StringUtils.isBlank(filename)) {
    174. return false;
    175. }
    176. //有后缀,是文件
    177. boolean contains = filename.contains(".");
    178. if (contains) {
    179. return false;
    180. }
    181. //是文件夹
    182. return true;
    183. }
    184. /**
    185. * 判断是否为目录、文件夹
    186. * @param path 文件路径
    187. * @return boolean
    188. */
    189. public static boolean isDirectoryFromPath(String path) {
    190. if (StringUtils.isBlank(path)) {
    191. return false;
    192. }
    193. String newPath = path.replace("\\", "/");
    194. if (newPath.contains("/")) {
    195. newPath = newPath.substring(newPath.lastIndexOf("/"));
    196. }
    197. //有后缀,是文件;
    198. boolean contains = newPath.contains(".");
    199. if (contains) {
    200. return false;
    201. }
    202. //是文件夹
    203. return true;
    204. }
    205. public static void main(String[] args) throws IOException {
    206. //文件夹复制
    207. String path = "templates";
    208. String newPath = "D:/tmp";
    209. FreeMarkerUtil.copyFolderFromJarCover(path, newPath);
    210. //文件复制
    211. String filePath = "application.properties";
    212. String newFilePath = "D:/tmp/application.properties";
    213. FreeMarkerUtil.copyFileFromJar(filePath, newFilePath);
    214. }
    215. }

    文件复制

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

    解决方案:

    1. //文件复制
    2. String filePath = "application.properties";
    3. String newFilePath = "D:/tmp/application.properties";
    4. FreeMarkerUtil.copyFileFromJar(filePath, newFilePath);
    1. /**
    2. * 复制文件(jar)
    3. *
    4. * @param path 源文件路径
    5. * @param newPath 新文件路径
    6. */
    7. public static void copyFileFromJar(String path, String newPath) throws IOException {
    8. //不读文件路径,直接读取文件流
    9. InputStream inputStream = ClassUtils
    10. .getDefaultClassLoader()
    11. .getResourceAsStream(path);
    12. write2File(inputStream, newPath);
    13. }

  • 相关阅读:
    vim安装AutoComplPop自动代码提示
    关于SID
    【凸优化学习笔记1】什么是优化、优化的数学表达形式
    软考考后常见问题详解~
    centOS7管理开放防火墙端口
    【架构】分布式与微服务架构解析
    CentOS7安装docker
    基于springcloud+web实现智慧养老平台系统项目【项目源码+论文说明】计算机毕业设计
    [百度飞桨EasyDL图文教程] 零代码也能用AI图像分类
    大数据技术现场工程师特色实训室解决方案
  • 原文地址:https://blog.csdn.net/qq_42277412/article/details/134413530