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无日志记录,是因为该异常直接在请求处理链路中抛出并返回给前端,未配置全局异常持久化逻辑的情况下,日志文件不会留存相关记录。
解决方案
禁止使用相对路径处理上传文件,必须指定明确授权的绝对存储路径:
- 在服务器上创建独立的上传文件存储目录(不要放在Tomcat部署目录或webapp下,避免重新部署WAR包时误删上传文件),为Tomcat启动用户授予该目录的读写权限,参考命令:
mkdir -p /data/app/upload chown tomcat:tomcat /data/app/upload # 替换为实际启动Tomcat的用户、用户组 chmod 755 /data/app/upload - 修改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); - 增加前置校验逻辑,方便提前定位路径、权限问题:
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
相关产品推荐
相关产品推荐

