Spring Boot项目写入静态文件遇FileNotFound错误求助
Spring Boot静态文件写入踩坑:FileNotFound异常解决
最近我在Spring Boot项目里尝试写入静态文件的时候碰到了个头疼的问题——访问localhost:8080/data/user.json能正常获取到文件,但前端触发写入操作时直接抛出了FileNotFound错误,项目结构如下:

先给大家看看相关的代码片段:
UserController代码
@Controller public class UserController { UserService userService = new UserServiceImpl(); @RequestMapping("/") public String getIndex(){ return "/index.html"; } @RequestMapping(value = "/register", method = RequestMethod.POST) @ResponseBody public Response<Boolean> register(String user, String password)throws IOException{ System.out.println("Get Register"); System.out.println("user" + " " + user); System.out.println("password" + " " + password); return userService.saveUser(user, password); } }
FileTool工具类代码
public class FileTool { public static boolean writeFile(String filePath, String content) throws IOException{ try { File file = new File(filePath); FileWriter writer = new FileWriter(file); PrintWriter printWriter = new PrintWriter(writer); printWriter.write(content); System.out.println(content); printWriter.println(); writer.close(); printWriter.close(); return true; }catch (IOException ex){ ex.printStackTrace(); } return false; } }
前端index.html代码
<!DOCTYPE html> <html> <input type="text" id="username" name="username" /> <input type="password" id="password" name="password"/> <input type="button" id="btn" value="login"/> <input type="button" id="btn2" value="register"/> <script src="http://code.jquery.com/jquery-latest.js"></script> <script type="text/javascript"> $(function () { $("#btn").click(function () { var user = $('#username').val(); var password = $('#password').val(); $.ajax({ type:"GET", url:"/login", dataType:"json", data : { user: user, password: password }, success : function (data) { if(data.success){ alert("Success!"); }else alert("Error!"); }, error : function () { alert("Network warning"); } }); }); $("#btn2").click(function () { var user = $('#username').val(); var password = $('#password').val(); $.ajax({ type:"POST", url:"/register", dataType:"json", data : { user: user, password: password }, success : function (data) { if(data.success){ alert("Success!"); }else alert("Error!"); }, error : function () { alert("Network warning"); } }); }); }); </script> </html>
当我在FileTool里用data/user.json这个路径时,抛出了如下错误:
java.io.FileNotFoundException: data/user.json (No such file or directory) at java.io.FileOutputStream.open0(Native Method) at java.io.FileOutputStream.open(FileOutputStream.java:270) at java.io.FileOutputStream.<init>(FileOutputStream.java:213) at java.io.FileOutputStream.<init>(FileOutputStream.java:162) at java.io.FileWriter.<init>(FileWriter.java:90) at com.utag.phase1.util.FileTool.writeFile(FileTool.java:12) at com.utag.phase1.dao.UserDaoImpl.saveUser(UserDaoImpl.java:20) at com.utag.phase1.service.Impl.UserServiceImpl.saveUser(UserServiceImpl.java:19) at com.utag.phase1.controller.UserController.register(UserController.java:41) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:870) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:776) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:881) at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:855) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745)
问题根源与解决办法
为什么会报错?
你用的data/user.json是相对路径,这个路径是相对于Spring Boot项目运行时的工作目录(比如IDE里运行时是项目根目录,打包成jar运行时是jar所在的目录),而不是你静态资源所在的src/main/resources/static目录。而且就算你找到这个路径,当项目打包成jar后,静态资源是在jar包内部的,根本没法直接写入压缩包里的文件。
正确的路径写法方案
1. 开发阶段用绝对路径(仅测试用)
直接写本地项目里静态文件的绝对路径,比如:
String filePath = "C:/your-project/src/main/resources/static/data/user.json";
缺点是换机器或者部署环境就失效,不适合生产。
2. 生产环境用外部目录(推荐)
在application.properties里配置一个外部存储路径:
file.storage.path=/opt/app-data/ # Linux环境 # 或者Windows环境:file.storage.path=C:/app-data/
然后在代码里注入这个配置,拼接路径写入:
@Service public class UserServiceImpl { @Value("${file.storage.path}") private String storagePath; public Response<Boolean> saveUser(String user, String password) throws IOException { String filePath = storagePath + "user.json"; // 调用FileTool写入 boolean result = FileTool.writeFile(filePath, "your-content"); return new Response<>(result); } }
这种方式不管项目怎么部署,只要配置好路径就能正常写入,而且不会影响jar包内部文件。
3. 开发阶段写入classpath路径(仅调试)
如果只是开发时想写入src/main/resources下的文件,可以通过类加载器获取classpath的真实路径:
String classpath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); String filePath = classpath + "static/data/user.json";
注意:打包成jar后这个方法无效,因为jar是压缩包,无法写入内部文件。
额外优化:资源自动关闭
你的FileTool里的流关闭可以用try-with-resources语法,避免忘记关闭导致资源泄漏:
public static boolean writeFile(String filePath, String content) throws IOException{ try (FileWriter writer = new FileWriter(filePath); PrintWriter printWriter = new PrintWriter(writer)) { printWriter.write(content); System.out.println(content); printWriter.println(); return true; } catch (IOException ex){ ex.printStackTrace(); return false; } }
内容的提问来源于stack exchange,提问作者Yu.Pan
相关产品推荐
相关产品推荐

