SpringBoot jar 中读取 resource 下的目录

读取目录下的文件列表

ResourceListUtil.listResourceDirFiles("view_node"),为读取 resource 目录下的 view_node 目录下的文件列表

@Slf4j
public class ResourceListUtil {
    /**
     * 修复版:列出 resource 下指定目录的所有文件(适配 Jar 包/开发环境)
     * 解决 view_node 目录在 Jar 包中无法读取文件的问题
     *
     * @param dirPath resource 下的目录路径,例如:view_node、config/
     * @return 文件的相对路径列表(如 view_node/test.txt、view_node/sub/file.json)
     */
    public static List<String> listResourceDirFiles(String dirPath) {
        List<String> fileList = new ArrayList<>();
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

        // 1. 统一路径格式:确保目录以 / 结尾,匹配规则包含所有子级
        String normalizedDir = StrUtil.addSuffixIfNot(dirPath, "/");
        String pattern = "classpath:" + normalizedDir + "**";

        try {
            Resource[] resources = resolver.getResources(pattern);
            for (Resource resource : resources) {
                // 2. 关键修复:不再依赖 isFile(),而是通过路径判断是否为文件(排除目录)
                String resourceUrl = resource.getURL().toString();
                String relativePath = extractRelativePath(resourceUrl);

                // 3. 过滤条件:
                // - 路径非空
                // - 属于目标目录(view_node)
                // - 不是目录(通过是否包含文件后缀/不含末尾/ 判断)
                if (StrUtil.isNotBlank(relativePath)
                        && relativePath.startsWith(normalizedDir)
                        && !relativePath.endsWith("/")  // 排除目录
                        && StrUtil.contains(relativePath, ".")) { // 确保是文件(有后缀)

                    fileList.add(relativePath);
                }
            }
        } catch (IOException e) {
            log.error("资源获取异常", e);
        }
        return fileList;
    }

    /**
     * 辅助方法:从资源 URL 中提取 resource 根目录下的相对路径
     * 兼容 Jar 包(jar:file:/xxx.jar!/BOOT-INF/classes!/view_node/test.txt)
     * 和开发环境(file:/xxx/target/classes/view_node/test.txt)
     * <p>
     * jar:nested:/D:/javaProject/company/xingtu-tygkpt/xingtu-tygkpt-application/target/application.jar/!BOOT-INF/classes/!/view_node/9_8396788870686588991.txt
     *
     * @param resourceUrl 资源的完整 URL
     * @return 相对路径(如 view_node/test.txt)
     */
    private static String extractRelativePath(String resourceUrl) {
        // Jar 包内路径特征:!/BOOT-INF/classes!/ (AI生成的判断)
        String jarMarker = "!/BOOT-INF/classes!/";
        // SpringBoot 的 Jar 包内路径特征
        String jarMarker2 = "/!BOOT-INF/classes/!/";
        // 开发环境路径特征:/target/classes/
        String devMarker = "/target/classes/";

        String relativePath = null;
        if (resourceUrl.contains(jarMarker)) {
            // Jar 包环境:截取 BOOT-INF/classes! 后的部分
            relativePath = StrUtil.subAfter(resourceUrl, jarMarker, false);
        } else if (resourceUrl.contains(jarMarker2)) {
            relativePath = StrUtil.subAfter(resourceUrl, jarMarker2, false);
        } else if (resourceUrl.contains(devMarker)) {
            // 开发环境:截取 target/classes/ 后的部分
            relativePath = StrUtil.subAfter(resourceUrl, devMarker, false);
        }

        // 处理编码问题(如路径中的空格、特殊字符)
        if (StrUtil.isNotBlank(relativePath)) {
            return relativePath;
        }
        return null;
    }

    /**
     * 扩展:仅列出指定目录下的一级文件(不包含子目录里的文件)
     *
     * @param dirPath 目标目录(如 view_node)
     * @return 一级文件列表
     */
    public static List<String> listFirstLevelFiles(String dirPath) {
        List<String> allFiles = listResourceDirFiles(dirPath);
        List<String> firstLevelFiles = new ArrayList<>();

        String normalizedDir = StrUtil.addSuffixIfNot(dirPath, "/");
        for (String file : allFiles) {
            // 一级文件:路径中仅包含目标目录的一个 /,无更多子目录
            String subPath = StrUtil.subAfter(file, normalizedDir, false);
            if (!StrUtil.contains(subPath, "/")) {
                firstLevelFiles.add(file);
            }
        }
        return firstLevelFiles;
    }
}

2. 读取压缩文件并解压缩

以 tar.gz 文件为例,可问 AI,我这也差不多是 ai 的逻辑,解压对应文件到内存,主要是我这 view_node 下的文件有点多,压缩起来比较小,也基本上不会改里面的内容

    /**
     * 解析处理节点数据
     */
    private void extractViewNode() throws Exception {
        Map<String, byte[]> stringMap = unzipTarGzFromJarToMemory("view_node.tar.gz");
        TEST_NODE_MAP.putAll(stringMap);
    }

    /**
     * tar.gz 解压数据到内存
     *
     * @param path resource path 路径
     * @return key 为路径,value 为内容字节数组
     */
    public static Map<String, byte[]> unzipTarGzFromJarToMemory(String path) throws Exception {
        Map<String, byte[]> fileMap = new HashMap<>();


        // 1. 流式解压:GZIP -> TAR -> 内存
        try (InputStream inputStream = ResourceUtil.getStream(path);
             // 先解压GZIP层
             GzipCompressorInputStream gzipInputStream = new GzipCompressorInputStream(inputStream);
             // 再读取TAR层
             TarArchiveInputStream tarInputStream = new TarArchiveInputStream(gzipInputStream)) {

            TarArchiveEntry entry;
            byte[] buffer = new byte[8192];

            // 2. 遍历TAR中的每一个文件
            while ((entry = tarInputStream.getNextEntry()) != null) {
                if (entry.isDirectory()) {
                    continue;
                }

                // 3. 将当前文件内容读取到字节数组
                try (ByteArrayOutputStream baos = new ByteArrayOutputStream((int) entry.getSize())) {
                    int len;
                    while ((len = tarInputStream.read(buffer)) != -1) {
                        baos.write(buffer, 0, len);
                    }

                    fileMap.put(entry.getName(), baos.toByteArray());
                }
            }
        }

        return fileMap;
    }
posted @ 2026-03-18 15:35  YangDanMua  阅读(2)  评论(0)    收藏  举报