V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
JYChiu
V2EX  ›  Java

Spring Boot 用 MultipartFile 上传图片文件空指针异常

  •  
  •   JYChiu · 2019-04-18 16:39:03 +08:00 · 1063 次点击
    这是一个创建于 2039 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目背景:Spring Boot 2.1.4 + mybatis

    方法:MultipartFile 上传图片文件到 FTP 服务器

    2019-04-18 16:10:46.418 ERROR 69079 --- [nio-8086-exec-4] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/t] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
    
    java.lang.NullPointerException: null
    	at com.example.boottest.service.serviceImpl.FileServiceImpl.upload(FileServiceImpl.java:24) ~[classes/:na]
    	at com.example.boottest.controller.background.ProductManageController.upload(ProductManageController.java:110) ~[classes/:na]
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
    	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
    	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_131]
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
    	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.17.jar:9.0.17]
    	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
    

    其中 FileServiceImpl 第一句就报空指针异常。。。

    @Service("iFileService")
    public class FileServiceImpl implements IFileService {
    
        Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);
        public String upload(MultipartFile file,String path){
    
            //  1. 定义新图片名、路径
    
            //下方空指针  
            String fileName=file.getOriginalFilename();
    
            String fileExtensionName=fileName.substring(fileName.lastIndexOf(".")+1);
    
            String uploadFileName= UUID.randomUUID().toString()+"."+fileExtensionName;
    
            logger.info("开始上传文件,上传的原始文件名:{},上传的路径:{},新文件名:{}",fileName,path,uploadFileName);
    
            //  2. 创建目录
    
            File fileDir=new File(path);
    
            if(!fileDir.exists()){
                //不存在就新建他
                fileDir.setWritable(true);
                fileDir.mkdirs();
            }
    
            //  3.  定义新文件
            File targetFile = new File(path,uploadFileName);
    
            //  4.  上传新文件
            try {
                file.transferTo(targetFile);  
                FTPUtil.uploadFile(Lists.newArrayList(targetFile)); 
                targetFile.delete();        
    
            } catch (IOException e) {
                logger.error("上传文件异常。",e);
                return null;
            }
    
            //  5.  返回新文件名
    
            return targetFile.getName();
        }
    }
    
    

    IFileService接口

    public interface IFileService {
        String upload(MultipartFile file, String path);
    }
    

    Controller

        @GetMapping(value = "/upload.do")
        public ServerResponse upload(HttpSession session, @RequestParam(value="upload_file",required = false) MultipartFile file, HttpServletRequest request){
            
            User user=(User)session.getAttribute(Const.CURRENT_USER);
            if(user == null){
                return ServerResponse.createByErrorMessage("请登录。");
            }
            if(iUserService.checkAdminRole(user).isSuccess()){
                String path=request.getSession().getServletContext().getRealPath("upload");
                //下方空指针  
                String targetFileName = iFileService.upload(file,path);
               
                String url = PropertiesUtils.getProperty("ftp.server.http.prefix")+targetFileName;
                Map fileMap = Maps.newHashMap();
                fileMap.put("uri",targetFileName);  
                fileMap.put("url",url);
                return ServerResponse.createBySuccess(fileMap);
            }
            return ServerResponse.createByErrorMessage("你没有权限。");
        }
       
    
    

    resources - templates - index.jsp

    <%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-type" content="text/html; charset=UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <title>图片上传</title>
    </head>
    <body>
    <form method="post" action="/manage/product/upload.do" enctype="multipart/form-data">
        <input type="file" name="upload_file"><br>
        <input type="submit" value="提交">
    </form>
    </body>
    </html>
    

    application.yml

    ftp:
      server:
        ip: xxx
        http.prefix: http://img.xxx.com/  
      user: xxx
      pass: xxx
    
    spring:
      servlet:
        multipart:
          max-file-size: 30MB    
          max-request-size: 30MB   
          enabled: true
      mvc:
        view:
          prefix: /templates/
          suffix: .jsp
    

    JSP 中name="upload_file" 与 Controller 中@RequestParam(value="upload_file",required = false)是对应起来的。

    不知道问题在哪,请各位大哥大姐雪中送炭啦。

    14 条回复    2019-04-19 01:52:26 +08:00
    fumichael
        1
    fumichael  
       2019-04-18 16:49:55 +08:00
    配置``` spring.servlet.multipart.enabled=true ```试试,
    JYChiu
        2
    JYChiu  
    OP
       2019-04-18 16:53:37 +08:00
    @fumichael 有哦,只是.yml 格式
    liuzuo
        3
    liuzuo  
       2019-04-18 16:55:05 +08:00
    file 本身应该就是空的。
    @GetMapping(value = "/upload.do")应该是 Postmapping 吧?

    然后不知道 spring 对 mvc 部署是否设置了对 multipart/form-data 表单的支持
    sonyxperia
        4
    sonyxperia  
       2019-04-18 16:56:26 +08:00
    前端提交的时候好像也要设置下,遇到过这个坑
    fumichael
        5
    fumichael  
       2019-04-18 16:58:35 +08:00
    @JYChiu #2 噢,还没留意,看来是 3 楼说的问题了
    smeraldo
        6
    smeraldo  
       2019-04-18 16:59:32 +08:00
    public String upload(MultipartFile file,String path)
    改成 public String upload(@RequestParam("upload_file") MultipartFile file,String path)
    kangzai50136
        8
    kangzai50136  
       2019-04-18 19:02:49 +08:00 via Android
    我也是用 multipartfile,用 postman 可以上传。
    putaozhenhaochi
        9
    putaozhenhaochi  
       2019-04-18 19:23:26 +08:00 via Android
    3 楼说的对
    JYChiu
        10
    JYChiu  
    OP
       2019-04-18 20:56:21 +08:00
    @kangzai50136 对,我用 Chrome 的 Restlet Client 有问题,POSTMAN 就可以,可能 Chrome 上插件太多影响了。
    JYChiu
        11
    JYChiu  
    OP
       2019-04-18 20:56:41 +08:00
    JYChiu
        12
    JYChiu  
    OP
       2019-04-18 20:58:42 +08:00
    @smeraldo 很奇怪,Controller 里改成 @RequestBody MultipartFile file 就能运行上传了。
    JYChiu
        13
    JYChiu  
    OP
       2019-04-18 20:59:35 +08:00
    @liuzuo 没错,谢了
    avk458
        14
    avk458  
       2019-04-19 01:52:26 +08:00
    jsp 你都知道用 post,controller 你用 GetMapping 接得到就怪了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   977 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 22:53 · PVG 06:53 · LAX 14:53 · JFK 17:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.