You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring MVC调试正常WAR部署Linux Tomcat文件上传报权限拒绝

问题根因

核心问题出在目标文件的路径构造逻辑上:

File f = new File(fileNameWithDateTime);

代码中仅传入文件名构造File对象,未指定绝对存储路径,这种写法会让Java将文件写入JVM进程的当前工作目录,两种启动模式下JVM工作目录、目录权限完全不同,直接导致了现象差异:

  • 通过STS启动项目时,JVM工作目录为STS配置的项目源码根目录,目录所有者为当前登录的开发用户,持有完整读写权限,因此上传流程正常
  • WAR包部署到Tomcat启动时,JVM工作目录默认为Tomcat的bin目录(部分启动场景下为Tomcat根目录或系统根目录,取决于启动脚本执行位置),该目录默认权限归属root或Tomcat专属运行用户,若Tomcat进程启动用户对该目录无写入权限,就会抛出Permission Denied错误。

从异常栈信息也能佐证这个判断:报错的文件路径仅显示08_06_2022-20220906112643.xlsx,没有任何路径前缀,说明确实是在相对路径对应的工作目录下创建文件失败。至于catalina.out无日志记录,是因为该异常直接在请求处理链路中抛出并返回给前端,未配置全局异常持久化逻辑的情况下,日志文件不会留存相关记录。

解决方案

禁止使用相对路径处理上传文件,必须指定明确授权的绝对存储路径:

  1. 在服务器上创建独立的上传文件存储目录(不要放在Tomcat部署目录或webapp下,避免重新部署WAR包时误删上传文件),为Tomcat启动用户授予该目录的读写权限,参考命令:
    mkdir -p /data/app/upload
    chown tomcat:tomcat /data/app/upload # 替换为实际启动Tomcat的用户、用户组
    chmod 755 /data/app/upload
    
  2. 修改Service层文件构造逻辑,拼接完整绝对路径生成目标文件,建议将存储路径配置到Spring配置文件中,不要硬编码:
    // 实际使用时从配置文件读取该路径
    String uploadBasePath = "/data/app/upload";
    String fileNameWithDateTime = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf(STR_DOT)) + STR_DASH
                + nOFACDateUtils.getCurrentTimeStamp() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(STR_DOT));
    // 传入父目录和文件名构造File对象,自动处理路径分隔符
    File f = new File(uploadBasePath, fileNameWithDateTime);
    
  3. 增加前置校验逻辑,方便提前定位路径、权限问题:
    File uploadDir = new File(uploadBasePath);
    if (!uploadDir.exists()) {
        uploadDir.mkdirs();
    }
    if (!uploadDir.canWrite()) {
        throw new RuntimeException("上传目录无写入权限:" + uploadBasePath);
    }
    
优化建议

现有代码中临时文件删除逻辑写在业务处理之后,若文件处理流程抛出异常,f.delete()不会执行,容易产生垃圾临时文件。建议将删除逻辑放到finally代码块中,或使用Java NIO的临时文件API自动回收资源。


内容的提问来源于stack exchange,提问作者Shimul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 09:24:35