• 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

    方式一 

    知道文件名的情况下,无需一层一层的遍历,将文件路径都指定好,然后根据流文件拷贝

    1. package com.aimsphm.practice;
    2. import lombok.extern.slf4j.Slf4j;
    3. import com.google.common.collect.Lists;
    4. import org.apache.commons.io.FileUtils;
    5. import org.springframework.beans.factory.annotation.Value;
    6. import org.springframework.boot.SpringApplication;
    7. import org.springframework.util.ObjectUtils;
    8. import javax.annotation.PostConstruct;
    9. import java.io.File;
    10. import java.io.IOException;
    11. import java.io.InputStream;
    12. import java.net.URL;
    13. import java.util.List;
    14. @Component
    15. public class App {
    16. public static void main(String[] args) {
    17. SpringApplication.run(App.class, args);
    18. }
    19. @Value("${customer.config.data-root:/usr/data/}")
    20. private String dataRoot;
    21. @PostConstruct
    22. public void initDatabase() {
    23. dataRoot = dataRoot.endsWith("/") ? dataRoot : dataRoot + "/";
    24. List fileList = getFiles();
    25. fileList.stream().filter(x -> !ObjectUtils.isEmpty(x)).forEach(x -> {
    26. try {
    27. URL resource = App.class.getClassLoader().getResource(x);
    28. InputStream inputStream = resource.openStream();
    29. if (ObjectUtils.isEmpty(inputStream)) {
    30. return;
    31. }
    32. File file = new File(dataRoot + x);
    33. if (!file.exists()) {
    34. FileUtils.copyInputStreamToFile(inputStream, file);
    35. }
    36. } catch (IOException e) {
    37. log.error("失败:",e)
    38. }
    39. });
    40. }
    41. private List getFiles() {
    42. return Lists.newArrayList(
    43. "db/practice.db",
    44. "local-data/0/p-1.jpg",
    45. "local-data/0/p-2.jpg",
    46. "local-data/0/p-3.jpg",
    47. "local-data/0/p-4.jpg",
    48. "local-data/1/meter-1.png",
    49. "local-data/-1/yw-1.png",
    50. "local-data/sound/test.txt",
    51. "local-data/multi/1/meter-multi.jpg",
    52. "local-data/multi/-1/yewei.png",
    53. "");
    54. }
    55. }

    方式二

    通过resource的方式,获取文件的描述信息,然后根据描述信息,获取文件的路径信息,然后通过拷贝流文件,将文件最终拷贝到指定的路径下

    1. package com.example.demo.test;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.springframework.core.io.Resource;
    4. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    5. import org.springframework.core.io.support.ResourcePatternResolver;
    6. import org.springframework.stereotype.Component;
    7. import java.io.File;
    8. import java.io.FileOutputStream;
    9. import java.io.IOException;
    10. import java.io.InputStream;
    11. import java.io.OutputStream;
    12. /**
    13. * 复制resource文件、文件夹
    14. *
    15. * @author MILLA
    16. */
    17. @Component
    18. @Slf4j
    19. public class JarFileUtil {
    20. public void copyFolderFromJar() throws IOException {
    21. this.copyFolderFromJar("templates", "/usr/data/files");
    22. }
    23. /**
    24. * 复制path目录下所有文件到指定的文件系统中
    25. *
    26. * @param path 文件目录 不能以/开头
    27. * @param newPath 新文件目录
    28. */
    29. public void copyFolderFromJar(String path, String newPath) throws IOException {
    30. path = preOperation(path, newPath);
    31. ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    32. //获取所有匹配的文件(包含根目录文件、子目录、子目录下的文件)
    33. Resource[] resources = resolver.getResources("classpath:" + path + "/**");
    34. //打印有多少文件
    35. for (Resource resource : resources) {
    36. //文件名
    37. //以jar包运行时,不能使用resource.getFile()获取文件路径、判断是否为文件等,会报错:
    38. //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
    39. //文件路径
    40. //file [/xxx/xxx]
    41. String description = resource.getDescription();
    42. description = description.replace("\\", "/");
    43. description = description.replace(path, "/");
    44. //保留 /xxx/xxx
    45. description = description.replaceAll("(.*\\[)|(]$)", "").trim();
    46. //以“文件目录”进行分割,获取文件相对路径
    47. //获取文件相对路径,/xxx/xxx
    48. //新文件路径
    49. String newFilePath = newPath + "/" + description;
    50. if (newFilePath.endsWith("/")) {
    51. File file = new File(newFilePath);
    52. //文件夹
    53. if (file.exists()) {
    54. boolean mkdir = file.mkdir();
    55. log.debug("路径[{}]创建是否成功状态:{} ", newFilePath, mkdir);
    56. }
    57. } else {
    58. //文件
    59. InputStream stream = resource.getInputStream();
    60. write2File(stream, newFilePath);
    61. }
    62. }
    63. }
    64. /**
    65. * 文件预处理
    66. *
    67. * @param path 原文件路径
    68. * @param newPath 目标路径
    69. * @return 新的路径字符串
    70. */
    71. private static String preOperation(String path, String newPath) {
    72. if (!new File(newPath).exists()) {
    73. boolean mkdir = new File(newPath).mkdir();
    74. log.debug("路径[{}]创建是否成功状态:{} ", newPath, mkdir);
    75. }
    76. if (path.contains("\\")) {
    77. path = path.replace("\\", "/");
    78. }
    79. //保证没有重复的/出现
    80. path = path.replaceAll("(?, "");
    81. if (path.startsWith("/")) {
    82. //以/开头,去掉/
    83. path = path.substring(1);
    84. }
    85. if (path.endsWith("/")) {
    86. //以/结尾,去掉/
    87. path = path.substring(0, path.length() - 1);
    88. }
    89. return path;
    90. }
    91. /**
    92. * 输入流写入文件
    93. *
    94. * @param is 输入流
    95. * @param filePath 文件保存目录路径
    96. * @throws IOException IOException
    97. */
    98. public static void write2File(InputStream is, String filePath) throws IOException {
    99. File destFile = new File(filePath);
    100. File parentFile = destFile.getParentFile();
    101. boolean mkdirs = parentFile.mkdirs();
    102. log.debug("路径[{}]创建是否成功状态:{} ", filePath, mkdirs);
    103. if (!destFile.exists()) {
    104. boolean newFile = destFile.createNewFile();
    105. log.debug("路径[{}]创建是否成功状态:{} ", destFile.getPath(), newFile);
    106. }
    107. OutputStream os = new FileOutputStream(destFile);
    108. int len = 8192;
    109. byte[] buffer = new byte[len];
    110. while ((len = is.read(buffer, 0, len)) != -1) {
    111. os.write(buffer, 0, len);
    112. }
    113. os.close();
    114. is.close();
    115. }
    116. public static void main(String[] args) throws IOException {
    117. //文件夹复制
    118. String path = "templates";
    119. String newPath = "D:/tmp";
    120. new JarFileUtil().copyFolderFromJar(path, newPath);
    121. }
    122. }

     如果开发中使用一些文件操作依赖,可简化代码如下

    1. --文件依赖 -->
    2. commons-fileupload
    3. commons-fileupload
    4. 1.3.3

     

    1. package com.example.demo.test;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.apache.commons.io.FileUtils;
    4. import org.springframework.core.io.Resource;
    5. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    6. import org.springframework.core.io.support.ResourcePatternResolver;
    7. import org.springframework.stereotype.Component;
    8. import javax.annotation.PostConstruct;
    9. import java.io.File;
    10. /**
    11. *

    12. * 功能描述:
    13. *

    14. *
    15. * @author MILLA
    16. * @version 1.0
    17. * @since 2024/05/31 16:30
    18. */
    19. @Slf4j
    20. @Component
    21. public class JarFileUtil{
    22. public static void main(String[] args) {
    23. JarFileUtilinit = new JarFileUtil();
    24. init.copyFile2Temp("//templates//shell//", "/usr/data/shell/files");
    25. }
    26. @PostConstruct
    27. public void copyFile2Temp() {
    28. }
    29. public void copyFile2Temp(String source, String target) {
    30. try {
    31. source = preOperation(source, target);
    32. ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    33. Resource[] resources = resolver.getResources(source + "/**");
    34. for (int i = 0; i < resources.length; i++) {
    35. Resource resource = resources[i];
    36. // resource.getFile() jar运行时候不能用该方法获取文件,因为jar的路径不对
    37. String description = resource.getDescription();
    38. description = description.replace("\\", "/");
    39. description = description.replace(source, "/");
    40. //保留 /xxx/xxx
    41. description = description.replaceAll("(.*\\[)|(]$)", "").trim();
    42. //以“文件目录”进行分割,获取文件相对路径
    43. //获取文件相对路径,/xxx/xxx
    44. //新文件路径
    45. String newFilePath = target + File.separator + description;
    46. File file = new File(newFilePath);
    47. if (newFilePath.endsWith("/")) {
    48. boolean mkdirs = file.mkdirs();
    49. log.debug("路径[{}]创建是否成功状态:{} ", newFilePath, mkdirs);
    50. } else {
    51. FileUtils.copyInputStreamToFile(resource.getInputStream(), file);
    52. }
    53. }
    54. } catch (Exception e) {
    55. log.error("文件拷贝异常:", e);
    56. }
    57. }
    58. private static String preOperation(String source, String target) {
    59. if (!new File(target).exists()) {
    60. boolean mkdir = new File(target).mkdir();
    61. log.debug("路径[{}]创建是否成功状态:{} ", target, mkdir);
    62. }
    63. if (source.contains("\\")) {
    64. source = source.replace("\\", "/");
    65. }
    66. //保证没有重复的/出现
    67. source = source.replaceAll("(?, "");
    68. if (source.startsWith("/")) {
    69. //以/开头,去掉/
    70. source = source.substring(1);
    71. }
    72. if (source.endsWith("/")) {
    73. //以/结尾,去掉/
    74. source = source.substring(0, source.length() - 1);
    75. }
    76. return source;
    77. }
    78. }

     

     通过这种方式,就能将正在运行的jar中的文件,拷贝到指定的路径下,记录备查

  • 相关阅读:
    爱宠-饲养小助手APP隐私政策
    【Redis】深入探索 Redis 的哨兵(Sentinel)机制原理,基于 Docker 模拟搭建 Redis 主从结构和哨兵分布式架构
    python 远程代码第一次推送
    Rowset Class
    k8s基于kubectl命令管理资源并分配
    C#:实现 Van Eck‘s sequence范·艾克序列算法(附完整源码)
    堆的使用(堆排序和Top-K问题)
    《程序员的七堂课》读书笔记:职业规划
    CI/CD:持续集成/持续部署
    聊聊logback的TimeBasedRollingPolicy
  • 原文地址:https://blog.csdn.net/hu10131013/article/details/139410136