java.nio.file.AccessDeniedException异常:图片读取权限问题排查
先梳理下你的问题场景:你用这段代码把Base64编码的图片存储到项目路径中:
if(imgString != null ) { BufferedImage image = null; byte[] imageByte; BASE64Decoder decoder = new BASE64Decoder(); imageByte = decoder.decodeBuffer(imgString); ByteArrayInputStream bis = new ByteArrayInputStream(imageByte); image = ImageIO.read(bis); bis.close(); String realPath = request.getSession().getServletContext().getRealPath("/resources/images/"+studentDetails.getFirstname()+".png"); File outputfile = new File(realPath); ImageIO.write(image, "png", outputfile); studentDetails.setStuImg(outputfile.toString()); }
但之后用下面的代码读取该图片时,在Files.readAllBytes这一行抛出了AccessDeniedException:
if(imagePath != null) { byte[] bytes = Files.readAllBytes(Paths.get(imagePath)); // 异常抛出点 encodedFile = Base64.getEncoder().encodeToString(bytes); }
对应的异常栈迹如下:
java.nio.file.AccessDeniedException: at sun.nio.fs.WindowsException.translateToIOException(Unknown Source) at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source) at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source) at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(Unknown Source) at java.nio.file.Files.newByteChannel(Unknown Source) at java.nio.file.Files.newByteChannel(Unknown Source) at java.nio.file.Files.readAllBytes(Unknown Source) at com.rasvek.cms.controller.StudentController.getStudentAdmissionByAdmissionId(StudentController.java:510) at com.rasvek.cms.controller.StudentController$$FastClassBySpringCGLIB$$cb2af793.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) at com.rasvek.cms.controller.StudentController$$EnhancerBySpringCGLIB$$537bb5b8.getStudentAdmissionByAdmissionId(<generated>) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) at org.apache.catalina.core.StandardContextVa
可能的原因分析
Tomcat运行用户权限不足
这是最常见的原因:你的Tomcat进程是用某个用户身份启动的,而这个用户没有读取目标图片文件或其父目录的权限。比如在Windows下,你用自己的账号开发时创建了文件,但Tomcat作为系统服务运行时用的是Local System或者其他受限账号,没有访问该文件的权限。文件被其他进程锁定
存储图片后,可能有其他程序(比如图片预览工具、杀毒软件)锁定了这个文件,导致Java进程无法打开读取。getRealPath()的路径局限性getRealPath()返回的是Web应用在服务器上的临时部署路径,在IDE内嵌Tomcat和生产环境独立Tomcat中路径可能不同,甚至容器重启后路径会变化。如果存储时的路径和读取时的路径不一致,也可能触发权限类的异常(虽然更常见的是文件不存在,但也有可能因为路径对应的目录权限问题报错)。存储时文件权限未正确设置
创建文件时,默认的权限设置可能仅允许创建文件的用户(比如你的开发账号)访问,而Tomcat运行用户不在权限范围内。
对应的解决方案
1. 检查并调整文件/目录权限
- 先确认图片的实际存储路径:在存储代码中添加
System.out.println(realPath);,打印出文件的绝对路径。 - 找到该文件,右键打开属性→安全选项卡,添加Tomcat运行的用户账号(比如
Local System或者你自定义的服务账号),并赋予它读取权限。 - 同时检查父目录(比如
resources/images/)的权限,确保该用户能遍历目录并访问其中的文件。
2. 替换getRealPath(),使用外部固定目录存储
getRealPath()依赖Web应用的部署路径,稳定性差,建议改用一个固定的外部目录来存储图片,比如D:/student_images/:
if(imgString != null ) { BufferedImage image = null; byte[] imageByte; BASE64Decoder decoder = new BASE64Decoder(); imageByte = decoder.decodeBuffer(imgString); ByteArrayInputStream bis = new ByteArrayInputStream(imageByte); image = ImageIO.read(bis); bis.close(); // 改用外部固定目录 String externalImageDir = "D:/student_images/"; File dir = new File(externalImageDir); // 确保目录存在,不存在则创建 if (!dir.exists()) { dir.mkdirs(); } String filePath = externalImageDir + studentDetails.getFirstname() + ".png"; File outputfile = new File(filePath); ImageIO.write(image, "png", outputfile); studentDetails.setStuImg(filePath); }
读取时直接使用这个外部路径即可,同时给D:/student_images/目录设置好Tomcat运行用户的读写权限。
3. 释放文件锁
关闭所有可能打开该图片的程序(比如Windows照片查看器),或者重启Tomcat服务,释放文件锁后再尝试读取。
4. 显式设置文件权限(可选)
在创建文件后,可以通过代码显式设置文件的可读权限,不过这个方法在不同操作系统下兼容性有限,更推荐直接调整系统目录权限:
// 写入图片后添加 outputfile.setReadable(true, false); // 第二个参数为false表示所有用户都可读 outputfile.setWritable(true, false); // 如果需要后续修改也可以加上
5. 验证路径一致性
在读取代码中添加System.out.println(imagePath);,对比存储时打印的路径,确保两者完全一致,避免因为路径拼写错误或环境变更导致读取错误的文件。
内容的提问来源于stack exchange,提问作者bharath

