在SpringMVC中@RequestParam为什么可以获得文件上传的数据?

发布于 2022-09-07 16:46:32 字数 1023 浏览 18 评论 0

在做springmvc文件上传的时候通常都会用这种方法获得上传的文件数据

//desc,file是页面上标签的id属性
    @RequestMapping(value="/testUpload",method=RequestMethod.POST)        
    public String testUpload(@RequestParam(value="desc",required=false) String desc, @RequestParam("file") MultipartFile multipartFile) throws IOException{  

    System.out.println("desc : "+desc);
    System.out.println("OriginalFilename : "+multipartFile.getOriginalFilename());
    
    InputStream inputStream = multipartFile.getInputStream();
    System.out.println("inputStream.available() : "+inputStream.available());
    System.out.println("inputStream : "+inputStream);
     
    return "success"; 
}

但是就我所知
@RequestParam是将请求参数映射到请求处理方法的形参中
常用来处理简单类型的绑定,用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容。
可是文件上传的时候Content-Type:为multipart/form-data,那么为什么此时@RequestParam可以得到文件数据,我个人的理解是MultipartFile 的原因,可能是因为springmvc的文件上传解析器起的作用,但是我还是没理解怎么做到的
可以认为@RequestParam也可以处理multipart/form-data吗。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

时光匆匆的小流年 2022-09-14 16:46:32

如果看下spring的源码会发现,spring在参数处理的时候会把Content-Type解析为MediaType,然后由spring框架中注册的具体RequestBodyAdviceAdapter来处理,不同的Content-Type会对应不同的RequestBodyAdviceAdapter。这个实现类实现了对上传文件的解析。

長街聽風 2022-09-14 16:46:32

spring mvc上传文件的时候,你需要在spring mvc配置文件中配置org.springframework.web.multipart.commons.CommonsMultipartResolver文件上传解析器。

1:当一个请求过来的时候,会调用DispatcherServlet类中的doDispatch(HttpServletRequest, HttpServletResponse)方法。

doDispatch方法体

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = processedRequest != request;

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest, false);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        String requestUri = urlPathHelper.getRequestUri(request);
                        logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                try {
                    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                }
                finally {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                }

                applyDefaultViewName(request, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Error err) {
            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                return;
            }
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }

2:在doDispatch方法体中调用checkMultipart(request)方法判断是否是文件上传的请求,判断是根据Content-type来的,如果是文件上传的请求,在checkMultipart(request)方法中就会调用在CommonsMultipartResolver中的方法进行文件解析(实际是调用apache commons-fileupload的文件长传插件),解析完成后,其实文件已经上传到服务器本地磁盘了,请看下面的代码片段。

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
        if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
            if (request instanceof MultipartHttpServletRequest) {
                logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
                        "this typically results from an additional MultipartFilter in web.xml");
            }
            else {
                return this.multipartResolver.resolveMultipart(request);
            }
        }
        // If not returned before: return original request.
        return request;
    }

在checkMultipart方法体中调用了CommonsMultipartResolver类的resolveMultipart方法去解析文件,该方法返回的是MultipartHttpServletRequest对象,该对象中存有已经上传的服务器本地的文件对象。

3:获取具体处理器的方法(mv = ha.handle(processedRequest, response, mappedHandler.getHandler()) 这行代码)。
如果是文件上传的话,processedRequest是上面的checkMultipart方法返回的对象。

实际调用的org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter类的handle方法,该方法中获取实际被调用的方法参数上有哪些注解,然后根据注解的一些配置,从processedRequest获取对应的值。

比如你上面的testUpload()方法。在AnnotationMethodHandlerAdapter的handle()的方法中会获取testUpload()的方法参数有@RequestParam这个注解,然后解析这个注解,从processedRequest请求中获取到desc的值,获取到名为file的文件,最后调用testUploaad()方法。具体是怎么解析的可以看看org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(Method, Object, NativeWebRequest, ExtendedModelMap)这个方法体。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文