jeecgboot/JeecgBoot · error · JeecgBootException

存储路径校验失败

Error message

存储路径校验失败: {storePath}

What it means

Thrown by FileDownloadUtils.download2DiskFromNet when File.getCanonicalPath() or getAbsolutePath() raises an IOException during the path-traversal check. The original IOException is wrapped as the cause of a JeecgBootException with the message '存储路径校验失败'. It indicates the filesystem could not resolve storePath, not that traversal was detected.

Solutions

  1. Ensure the target storage directory exists and the application process has read/write permission on it before invoking download2DiskFromNet.
  2. Call Files.createDirectories(Path.of(storePath).getParent()) prior to the download.
  3. Verify the storePath configuration value in application yml resolves to a real, writable directory.
  4. Catch JeecgBootException at the caller and surface a user-friendly message, logging the wrapped IOException cause.

Example fix

// before
FileDownloadUtils.download2DiskFromNet(fileUrl, storePath);

// after
Path parent = Path.of(storePath).toAbsolutePath().getParent();
if (!Files.exists(parent)) {
    Files.createDirectories(parent);
}
FileDownloadUtils.download2DiskFromNet(fileUrl, storePath);
Defensive patterns

Strategy: validation

Validate before calling

Path store = Path.of(storePath);
Path parent = store.toAbsolutePath().getParent();
if (!Files.isDirectory(parent)) {
    Files.createDirectories(parent);
}
if (!Files.isWritable(parent)) {
    throw new IllegalStateException("存储目录不可写: " + parent);
}

Type guard

null

Try / catch

try {
    FileDownloadUtils.download2DiskFromNet(fileUrl, storePath);
} catch (JeecgBootException e) {
    log.error("存储路径校验失败", e);
    return Result.error("存储路径不可用,请联系管理员");
}

Prevention

When it happens

Trigger: Calling download2DiskFromNet with a storePath whose parent directory does not exist, contains illegal characters (Windows), points to an unreadable location, or triggers a filesystem-level I/O error during canonical-path resolution.

Common situations: Storage directory not yet created on a fresh deployment; permission denied on the configured upload path; storePath derived from a misconfigured property that points to a non-existent drive or mount; Windows path with reserved/illegal characters.

Related errors


AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14). Data as JSON: /api/errors/69143f2b756eb807. Report an issue: GitHub.

Appendix: source

Thrown at jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileDownloadUtils.java:144

     * 下载网络资源到磁盘
     *
     * @param fileUrl
     * @param storePath
     * @author chenrui
     * @date 2024/1/19 10:09
     */
    public static String download2DiskFromNet(String fileUrl, String storePath) {
        //update-begin---author:liusq ---date:2026-03-30  for:【issues/9437】修复download2DiskFromNet storePath路径遍历漏洞(CWE-22)-----------
        // 路径遍历校验:拦截 ../ 等遍历字符,并确保规范化路径与原始路径一致
        SsrfFileTypeFilter.checkPathTraversal(storePath);
        try {
            String canonicalPath = new File(storePath).getCanonicalPath();
            String absolutePath = new File(storePath).getAbsolutePath();
            if (!canonicalPath.equals(absolutePath)) {
                throw new JeecgBootException("非法存储路径,路径包含遍历字符: " + storePath);
            }
        } catch (IOException e) {
            throw new JeecgBootException("存储路径校验失败: " + storePath, e);
        }
        //update-end---author:liusq ---date:2026-03-30  for:【issues/9437】修复download2DiskFromNet storePath路径遍历漏洞(CWE-22)-----------
        //update-begin---author:zhangdaihao ---date:2026-04-15  for:【issues/9553】下载网络资源前增加SSRF校验-----------
        SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);
        //update-end---author:zhangdaihao ---date:2026-04-15  for:【issues/9553】下载网络资源前增加SSRF校验-----------
        try {
            URL url = new URL(fileUrl);
            URLConnection conn = url.openConnection();
            // 设置超时间为3秒
            conn.setConnectTimeout(3 * 1000);
            // 防止屏蔽程序
            conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            // 确保目录存在
            File file = ensureDestFileDir(storePath);
            try (InputStream inStream = conn.getInputStream();
                 FileOutputStream fs = new FileOutputStream(file);) {
                int byteread;
                byte[] buffer = new byte[1204];

View on GitHub (pinned to 96fb33f5ec)