最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot文件上傳的原理解析

 更新時間:2023年10月10日 10:27:42   作者:劉婉晴  
這篇文章主要介紹了SpringBoot文件上傳的原理解析,SpringBoot 文件上傳是一種方便快捷的方式,可以將文件上傳到服務(wù)器,通過使用SpringBoot的文件上傳功能,可以輕松地實現(xiàn)文件上傳功能,需要的朋友可以參考下

一、請求進(jìn)入,使用文件上傳解析器判斷并封裝

文件上傳解析器:只能接收標(biāo)準(zhǔn)的 Servlet 方式上傳的文件

@ConditionalOnMissingBean({MultipartResolver.class}) 判斷容器中無文件上傳解析器,若無自動創(chuàng)建

    @ConditionalOnMissingBean({MultipartResolver.class}) // 判斷注解
    public StandardServletMultipartResolver multipartResolver() {
        StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
        multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
        return multipartResolver;
    }

DispatcherServlet.class —— doDispatch()方法

與文件上傳相關(guān)功能有關(guān)的語句,請見下面的注釋:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;
                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request; // 判斷是否是文件上傳請求(封裝好的文件上傳請求與原請求不相等,則判斷是文件上傳請求)
                    mappedHandler = this.getHandler(processedRequest); // 找誰能處理文件上傳請求
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }
        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }
        }
    }

(1) processedRequest = this.checkMultipart(request) —— 用于判斷是否是文件上傳請求

Step Into 查看:發(fā)現(xiàn)使用 multipartResolver 判斷是否是文件上傳請求

   protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
        if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) { // 判斷是否是文件上傳請求
            if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
                if (request.getDispatcherType().equals(DispatcherType.REQUEST)) {
                    this.logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
                }
            } else if (this.hasMultipartException(request)) {
                this.logger.debug("Multipart resolution previously failed for current request - skipping re-resolution for undisturbed error rendering");
            } else {
                try {
                    return this.multipartResolver.resolveMultipart(request); // 解析文件上傳請求
                } catch (MultipartException var3) {
                    if (request.getAttribute("javax.servlet.error.exception") == null) {
                        throw var3;
                    }
                }
                this.logger.debug("Multipart resolution failed for error dispatch", var3);
            }
        }
        return request;
    }

StepInto—— multipartResolver 判斷是否是文件上傳請求方式

用 String 工具類判斷是否以 multipart/ 開頭(這也解釋了為什么我們在寫前端表單接收文件時,必須使用multipart)

    public boolean isMultipart(HttpServletRequest request) {
        return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
    }

(2) this.multipartResolver.resolveMultipart(request) 解析文件上傳請求

其將文件上傳請求封裝為 MultipartHttpServletRequest 類返回

    public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
        return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
    }

二、參數(shù)解析器解析請求中的文件內(nèi)容封裝成 MultipartFile

文件請求參數(shù)解析器:

在這里插入圖片描述

(1) InvocableHandlerMethod.class 找到參數(shù)解析器,執(zhí)行文件上傳代理

  public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest servletRequest = (HttpServletRequest)request.getNativeRequest(HttpServletRequest.class);
        Assert.state(servletRequest != null, "No HttpServletRequest");
        RequestPart requestPart = (RequestPart)parameter.getParameterAnnotation(RequestPart.class);
        boolean isRequired = (requestPart == null || requestPart.required()) && !parameter.isOptional();
        String name = this.getPartName(parameter, requestPart);
        parameter = parameter.nestedIfOptional();
        Object arg = null;
        Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest); // 文件上傳代理
        if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
            arg = mpArg;
        } else {
            try {
                HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, name);
                arg = this.readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());
                if (binderFactory != null) {
                    WebDataBinder binder = binderFactory.createBinder(request, arg, name);
                    if (arg != null) {
                        this.validateIfApplicable(binder, parameter);
                        if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
                            throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
                        }
                    }
                    if (mavContainer != null) {
                        mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
                    }
                }
            } catch (MultipartException | MissingServletRequestPartException var13) {
                if (isRequired) {
                    throw var13;
                }
            }
        }
  }

(2)確定每個參數(shù)的值

       Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);

(3)解析所有參數(shù)

           for(int i = 0; i < parameters.length; ++i) {
                MethodParameter parameter = parameters[i];
                parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
                args[i] = findProvidedArgument(parameter, providedArgs);
                if (args[i] == null) {
                    if (!this.resolvers.supportsParameter(parameter)) {
                        throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
                    }
                    try {
                        args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                    } catch (Exception var10) {
                        if (logger.isDebugEnabled()) {
                            String exMsg = var10.getMessage();
                            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                                logger.debug(formatArgumentError(parameter, exMsg));
                            }
                        }
                        throw var10;
                    }
                }
            }

三、將 request 文件封裝為一個 Map (Map<String, MultipartFile>)

AbstractMultipartHttpServletRequest.class

    public List<MultipartFile> getFiles(String name) {
        List<MultipartFile> multipartFiles = (List)this.getMultipartFiles().get(name);
        return multipartFiles != null ? multipartFiles : Collections.emptyList();
    }
public Map<String, MultipartFile> getFileMap() {
        return this.getMultipartFiles().toSingleValueMap();
    }

總結(jié):

  • 請求進(jìn)入,使用文件上傳解析器判斷(isMultipart)并封裝(resolveMultipart,返回MultipartHttpServletRequest)文件上傳請求
  • 參數(shù)解析器解析請求中的文件內(nèi)容封裝成 MultipartFile
  • 將 request 文件封裝為一個 Map (Map<String, MultipartFile>)

通過觀察源碼,可以得到許多 SpringBoot 為我們封裝好的文件工具類,如 FileCopyUtils 實現(xiàn)文件流的拷貝

到此這篇關(guān)于SpringBoot文件上傳的原理解析的文章就介紹到這了,更多相關(guān)SpringBoot文件上傳內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于Java如何正確地實現(xiàn)方法重載詳解

    關(guān)于Java如何正確地實現(xiàn)方法重載詳解

    在一個類中,可以定義多個構(gòu)造方法,這叫做方法的重載!但是關(guān)于方法重載,具有有哪些要求和細(xì)節(jié)?在今天的這篇文章中,小編給大家詳細(xì)說說方法重載相關(guān)的內(nèi)容,需要的朋友可以參考下
    2023-05-05
  • 詳解springboot整合mongodb

    詳解springboot整合mongodb

    本篇文章主要介紹了詳解springboot整合mongodb,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • SpringBoot程序打包失敗(.jar中沒有主清單屬性)

    SpringBoot程序打包失敗(.jar中沒有主清單屬性)

    在學(xué)習(xí)SpringBoot,打包SpringBoot程序后,在cmd運行出現(xiàn)了 某某某.jar中沒有注清單屬性,本文就來介紹一下原因以及解決方法,感興趣的可以了解一下
    2023-06-06
  • python實戰(zhàn)之德州撲克第一步-發(fā)牌

    python實戰(zhàn)之德州撲克第一步-發(fā)牌

    這篇文章主要介紹了python實戰(zhàn)之德州撲克第一步-發(fā)牌,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)python的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • Springboot接收前端的Json但是為null問題

    Springboot接收前端的Json但是為null問題

    這篇文章主要介紹了Springboot接收前端的Json但是為null問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教<BR>
    2024-05-05
  • SpringBoot使用Cache集成Redis做緩存的保姆級教程

    SpringBoot使用Cache集成Redis做緩存的保姆級教程

    Spring Cache是Spring框架提供的一個緩存抽象層,它簡化了緩存的使用和管理,Spring Cache默認(rèn)使用服務(wù)器內(nèi)存,并無法控制緩存時長,查找緩存中的數(shù)據(jù)比較麻煩,本文已常用的Redis作為緩存中間件作為示例,詳細(xì)講解項目中如何使用Cache提高系統(tǒng)性能,需要的朋友可以參考下
    2025-01-01
  • Java實現(xiàn)英文猜詞游戲的示例代碼

    Java實現(xiàn)英文猜詞游戲的示例代碼

    這篇文章主要介紹了如何用Java編寫一個英文猜詞游戲,可以用來背英語單詞。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-02-02
  • mybatis-plus之如何根據(jù)數(shù)據(jù)庫主鍵定義字段類型

    mybatis-plus之如何根據(jù)數(shù)據(jù)庫主鍵定義字段類型

    這篇文章主要介紹了mybatis-plus之如何根據(jù)數(shù)據(jù)庫主鍵定義字段類型問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Java FastJson使用教程

    Java FastJson使用教程

    這篇文章主要介紹了如何使用FastJson,幫助大家將 Java 對象轉(zhuǎn)換為 JSON 格式,感興趣的朋友可以了解下
    2020-10-10
  • Java編程實現(xiàn)從給定范圍內(nèi)隨機(jī)N個不重復(fù)數(shù)生成隨機(jī)數(shù)的方法小結(jié)

    Java編程實現(xiàn)從給定范圍內(nèi)隨機(jī)N個不重復(fù)數(shù)生成隨機(jī)數(shù)的方法小結(jié)

    這篇文章主要介紹了Java編程實現(xiàn)從給定范圍內(nèi)隨機(jī)N個不重復(fù)數(shù)生成隨機(jī)數(shù)的方法,結(jié)合實例形式較為詳細(xì)的分析了java根據(jù)指定范圍生成不重復(fù)隨機(jī)數(shù)的相關(guān)操作技巧,需要的朋友可以參考下
    2017-04-04

最新評論

柯坪县| 辽阳市| 文昌市| 舒兰市| 游戏| 尼玛县| 肃宁县| 本溪市| 隆昌县| 庄河市| 巴塘县| 射阳县| 昭觉县| 二手房| 祁东县| 尉犁县| 惠州市| 花莲市| 兖州市| 麦盖提县| 岐山县| 新竹市| 基隆市| 金沙县| 建平县| 吴川市| 金坛市| 江油市| 安图县| 政和县| 逊克县| 大邑县| 西吉县| 兰坪| 宁武县| 隆安县| 合水县| 广丰县| 美姑县| 梧州市| 巩义市|