MultipartConfigElement与SpringMVC中DispartcherServlet对MutipartFile的处理_Wu_Menghao的博客-程序员秘密

技术标签: 源码学习  

MultipartConfigElement

MultipartConfigElement 是 javax.servlet 包中的是 JavaEE Servlet 规范定义的标准包。

该类定义了Http服务上传文件存储位置、最大文件大小、最大请求的长度

javax.servlet.MultipartConfigElement

public class MultipartConfigElement {

    private final String location;// = "";
    private final long maxFileSize;// = -1;
    private final long maxRequestSize;// = -1;
    private final int fileSizeThreshold;// = 0;

  。。。。
}

以Springboot Tomcat为例,Tomcat 的 Request 请求会被传递到 SpringMVC 的 DispatcherServlet 中,在 doDispartch() 方法会 Request 的 getParts() 方法被调用, 会去获取 MultipartConfigElement 对象,从中获取存储位置将上传的文件存储在临时存储位置,这些文件以List<Part>的形式存在于 StandardMuiltpartHttpSevrletRequest 中返回给 DispatcherServlet 继续处理。在整个请求处理完成后,DispatcherServlet 会调用 StandardServletMultipartResolver 的 cleanupMultipart() 的方法,清理掉所有的缓存文件。

org.springframework.web.servlet.DispatcherServlet 

	/**
	 * Process the actual dispatching to the handler.
	 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
	 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
	 * to find the first that supports the handler class.
	 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
	 * themselves to decide which methods are acceptable.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception in case of any kind of processing failure
	 */
	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);                    // 将请求转化为 multipart request 让多媒体处理器可生效
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == 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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

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

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest); // 请求处理完成后清除多媒体文件
				}
			}
		}
	}


	/** 将请求转化为 multipart request 让多媒体处理器可生效
	 * Convert the request into a multipart request, and make multipart resolver available.
	 * <p>If no multipart resolver is set, simply use the existing request.
	 * @param request current HTTP request
	 * @return the processed request (multipart wrapper if necessary)
	 * @see MultipartResolver#resolveMultipart
	 */
	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)) {
					logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
				}
			}
			else if (hasMultipartException(request)) {
				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 ex) {
					if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
						logger.debug("Multipart resolution failed for error dispatch", ex);
						// Keep processing error dispatch with regular request handle below
					}
					else {
						throw ex;
					}
				}
			}
		}
		// If not returned before: return original request.
		return request;
	}

org.springframework.web.multipart.support.StandardServletMultipartResolver 

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

org.springframework.web.multipart.support.StandardMultipartHttpServletRequest

	/**
	 * Create a new StandardMultipartHttpServletRequest wrapper for the given request.
	 * @param request the servlet request to wrap
	 * @param lazyParsing whether multipart parsing should be triggered lazily on
	 * first access of multipart files or parameters
	 * @throws MultipartException if an immediate parsing attempt failed
	 * @since 3.2.9
	 */
	public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
			throws MultipartException {

		super(request);
		if (!lazyParsing) {
			parseRequest(request);
		}
	}


	private void parseRequest(HttpServletRequest request) {
		try {
			Collection<Part> parts = request.getParts();
			this.multipartParameterNames = new LinkedHashSet<>(parts.size());
			MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
			for (Part part : parts) {
				String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
				ContentDisposition disposition = ContentDisposition.parse(headerValue);
				String filename = disposition.getFilename();
				if (filename != null) {
					if (filename.startsWith("=?") && filename.endsWith("?=")) {
						filename = MimeDelegate.decode(filename);
					}
					files.add(part.getName(), new StandardMultipartFile(part, filename));
				}
				else {
					this.multipartParameterNames.add(part.getName());
				}
			}
			setMultipartFiles(files);
		}
		catch (Throwable ex) {
			handleParseFailure(ex);
		}
	}

	protected void handleParseFailure(Throwable ex) {
		String msg = ex.getMessage();
		if (msg != null && msg.contains("size") && msg.contains("exceed")) {
			throw new MaxUploadSizeExceededException(-1, ex);
		}
		throw new MultipartException("Failed to parse multipart servlet request", ex);
	}

 org.apache.catalina.connector.Request 

/**
 * {@inheritDoc}
 */
@Override
public Collection<Part> getParts() throws IOException, IllegalStateException,
        ServletException {

    parseParts(true);

    if (partsParseException != null) {
        if (partsParseException instanceof IOException) {
            throw (IOException) partsParseException;
        } else if (partsParseException instanceof IllegalStateException) {
            throw (IllegalStateException) partsParseException;
        } else if (partsParseException instanceof ServletException) {
            throw (ServletException) partsParseException;
        }
    }

    return parts;
}

private void parseParts(boolean explicit) {

    // Return immediately if the parts have already been parsed
    if (parts != null || partsParseException != null) {
        return;
    }

    Context context = getContext();
    MultipartConfigElement mce = getWrapper().getMultipartConfigElement();

    if (mce == null) {
        if(context.getAllowCasualMultipartParsing()) {
            mce = new MultipartConfigElement(null, connector.getMaxPostSize(),
                    connector.getMaxPostSize(), connector.getMaxPostSize());
        } else {
            if (explicit) {
                partsParseException = new IllegalStateException(
                        sm.getString("coyoteRequest.noMultipartConfig"));
                return;
            } else {
                parts = Collections.emptyList();
                return;
            }
        }
    }

    Parameters parameters = coyoteRequest.getParameters();
    parameters.setLimit(getConnector().getMaxParameterCount());

    boolean success = false;
    try {
        File location;
        String locationStr = mce.getLocation();
        if (locationStr == null || locationStr.length() == 0) {
            location = ((File) context.getServletContext().getAttribute(
                    ServletContext.TEMPDIR));
        } else {
            // If relative, it is relative to TEMPDIR
            location = new File(locationStr);
            if (!location.isAbsolute()) {
                location = new File(
                        (File) context.getServletContext().getAttribute(ServletContext.TEMPDIR),
                        locationStr).getAbsoluteFile();
            }
        }

        if (!location.exists() && context.getCreateUploadTargets()) {
            log.warn(sm.getString("coyoteRequest.uploadCreate",
                    location.getAbsolutePath(), getMappingData().wrapper.getName()));
            if (!location.mkdirs()) {
                log.warn(sm.getString("coyoteRequest.uploadCreateFail",
                        location.getAbsolutePath()));
            }
        }

        if (!location.isDirectory()) {
            parameters.setParseFailedReason(FailReason.MULTIPART_CONFIG_INVALID);
            partsParseException = new IOException(
                    sm.getString("coyoteRequest.uploadLocationInvalid",
                            location));
            return;
        }


        // Create a new file upload handler
        DiskFileItemFactory factory = new DiskFileItemFactory();
        try {
            factory.setRepository(location.getCanonicalFile());
        } catch (IOException ioe) {
            parameters.setParseFailedReason(FailReason.IO_ERROR);
            partsParseException = ioe;
            return;
        }
        factory.setSizeThreshold(mce.getFileSizeThreshold());

        ServletFileUpload upload = new ServletFileUpload();
        upload.setFileItemFactory(factory);
        upload.setFileSizeMax(mce.getMaxFileSize());
        upload.setSizeMax(mce.getMaxRequestSize());

        parts = new ArrayList<>();
        try {
            List<FileItem> items =
                    upload.parseRequest(new ServletRequestContext(this));
            int maxPostSize = getConnector().getMaxPostSize();
            int postSize = 0;
            Charset charset = getCharset();
            for (FileItem item : items) {
                ApplicationPart part = new ApplicationPart(item, location);
                parts.add(part);
                if (part.getSubmittedFileName() == null) {
                    String name = part.getName();
                    String value = null;
                    try {
                        value = part.getString(charset.name());
                    } catch (UnsupportedEncodingException uee) {
                        // Not possible
                    }
                    if (maxPostSize >= 0) {
                        // Have to calculate equivalent size. Not completely
                        // accurate but close enough.
                        postSize += name.getBytes(charset).length;
                        if (value != null) {
                            // Equals sign
                            postSize++;
                            // Value length
                            postSize += part.getSize();
                        }
                        // Value separator
                        postSize++;
                        if (postSize > maxPostSize) {
                            parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
                            throw new IllegalStateException(sm.getString(
                                    "coyoteRequest.maxPostSizeExceeded"));
                        }
                    }
                    parameters.addParameter(name, value);
                }
            }

            success = true;
        } catch (InvalidContentTypeException e) {
            parameters.setParseFailedReason(FailReason.INVALID_CONTENT_TYPE);
            partsParseException = new ServletException(e);
        } catch (SizeException e) {
            parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
            checkSwallowInput();
            partsParseException = new IllegalStateException(e);
        } catch (FileUploadException e) {
            parameters.setParseFailedReason(FailReason.IO_ERROR);
            partsParseException = new IOException(e);
        } catch (IllegalStateException e) {
            // addParameters() will set parseFailedReason
            checkSwallowInput();
            partsParseException = e;
        }
    } finally {
        // This might look odd but is correct. setParseFailedReason() only
        // sets the failure reason if none is currently set. This code could
        // be more efficient but it is written this way to be robust with
        // respect to changes in the remainder of the method.
        if (partsParseException != null || !success) {
            parameters.setParseFailedReason(FailReason.UNKNOWN);
        }
    }
}

org.apache.tomcat.util.http.fileupload.FileUploadBase 


    /**
     * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
     * compliant <code>multipart/form-data</code> stream.
     *
     * @param ctx The context for the request to be parsed.
     *
     * @return A list of <code>FileItem</code> instances parsed from the
     *         request, in the order that they were transmitted.
     *
     * @throws FileUploadException if there are problems reading/parsing
     *                             the request or storing files.
     */
    public List<FileItem> parseRequest(RequestContext ctx)
            throws FileUploadException {
        List<FileItem> items = new ArrayList<>();
        boolean successful = false;
        try {
            FileItemIterator iter = getItemIterator(ctx);
            FileItemFactory fac = getFileItemFactory();
            final byte[] buffer = new byte[Streams.DEFAULT_BUFFER_SIZE];
            if (fac == null) {
                throw new NullPointerException("No FileItemFactory has been set.");
            }
            while (iter.hasNext()) {
                final FileItemStream item = iter.next();
                // Don't use getName() here to prevent an InvalidFileNameException.
                final String fileName = ((FileItemStreamImpl) item).getName();
                FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
                                                   item.isFormField(), fileName);
                items.add(fileItem);
                try {
                    Streams.copy(item.openStream(), fileItem.getOutputStream(), true, buffer);        // 文件最终上传到temp目录到地方
                } catch (FileUploadIOException e) {
                    throw (FileUploadException) e.getCause();
                } catch (IOException e) {
                    throw new IOFileUploadException(String.format("Processing of %s request failed. %s",
                                                           MULTIPART_FORM_DATA, e.getMessage()), e);
                }
                final FileItemHeaders fih = item.getHeaders();
                fileItem.setHeaders(fih);
            }
            successful = true;
            return items;
        } catch (FileUploadIOException e) {
            throw (FileUploadException) e.getCause();
        } catch (IOException e) {
            throw new FileUploadException(e.getMessage(), e);
        } finally {
            if (!successful) {
                for (FileItem fileItem : items) {
                    try {
                        fileItem.delete();
                    } catch (Exception ignored) {
                        // ignored TODO perhaps add to tracker delete failure list somehow?
                    }
                }
            }
        }
    }

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weweeeeeeee/article/details/112846570

智能推荐

redhat6.3 安装yum命令_wexin_bai750259172的博客-程序员秘密

RHEL6解决无法使用YUM源问题RHEL的YUM源需要注册用户才能更新使用,由于CentOS和RHEL基本没有区别,并且CentOS已经被REHL收购。所以将RHEL的YUM源替换为CentOS即可。问题如下:[[email protected] ~]# yum repolistLoaded plugins: product-id, refresh-packagekit, security,

MFC架构之CObject类_FlowShell的博客-程序员秘密

<br />      CObject是大多数MFC类的根类或基类。CObject类有很多有用的特性:对运行时类信息的支持,对动态创建的支持,对串行化的支持,对象诊断输出,等等。MFC从CObject派生出许多类,具备其中的一个或者多个特性。程序员也可以从CObject类派生出自己的类,利用CObject类的这些特性。<br />     本文讨论MFC如何设计CObject类的这些特性。首先,考察CObject类的定义,分析其结构和方法(成员变量和成员函数)对CObject特性的支持。然后,讨论CObje

二进制数的原码反码与补码_weixin_30410999的博客-程序员秘密

众所周知,二进制是我们计算机语法的基本组成,他以0和1为基础,构建了我们多样神奇的信息世界。因此说,二进制对于我们是十分重要的。对于二进制来说,他在计算机中有三中表示方法:原码、反码和补码。这三种码的区别,就是对于正负表示不同的区别。对于原码来说,他对于正负的表示方法是在整个二进制数前加0或1。0用来表示正号,1用来表示负号。比如,一个正二进制数+1101表示为01101,负二...

5G-SUPI-SUPC-IMSI_imsi supi_weixin_37646183的博客-程序员秘密

0x00 IMSI &amp; IMSI-Catcher我们以前担心的手机泄漏个人位置隐私的问题,也就是在2G/3G/4G一直存在的IMSI Catcher问题,终于有望在5G标准里得到彻底解决啦! 这个问题在每次制定新一代网络标准的时候,都要争论一回。这是网络功能性、成本与安全性之间的斗争。在5G的第一版标准,Release15,关于安全的标准[1]中,IMSI加密是最大的亮点。在2/3/4...

计算机应用技术作业,计算机应用与技术-作业题.doc_weixin_39747630的博客-程序员秘密

文档介绍:计算机应用与技术网上作业题第一部分计算机基础知识作业题一、选择题1.第二代电子计算机使用的电子器件是____。A)电子管B)晶体管C)集成电路D)超大规模集成电路2.计算机病毒是指____。A)带细菌的磁盘B)已损坏的磁盘C)具有破坏性的特制程序D)被破坏的程序3.与十六进制数AB等值的十进制数是____。A)175B)176C)177D)1714.计算机中所有信息的存储都采用____。...

随便推点

常见激光雷达分类_光学测距的单点和面阵的区别_阿木实验室的博客-程序员秘密

一、激光雷达的原理激光雷达的原理类似于声呐,只不过这里我们用光代替声音,来衡量激光雷达与障碍物之间的距离。主要工作原理是向目标发射激光束(单线/多线),然后将反射信号与发射信号比较, 分析信号的折返时间(TOF)或频率差(多普勒频移),即可获得目标距离等相关参数。为什么叫激光雷达?激光是大量原子受激辐射所产生的发光行为,激光在传播中始终像一条笔直的细线,发散的角度极小。如果一个激光雷达能够在同一个空间内,按照设定好的角度发射无数条激光,就能得到无数基于障碍物的反射信号。比如一条线..

计算机图形学实验一中点算法绘制圆_利用中点扫描算法,对单位圆进行绘制_机器王德发的博客-程序员秘密

java实现计算机图形学中点画圆算法计算机图形学实验一采用中点画圆算法原理绘制1/8圆弧,然后利用圆的高度对称性,基于java的Graphics类,调用getGraphics()方法,在GUI界面上绘图,由于java的坐标位于左上角,所以我们可以将圆心移动至面板中心再进行算法进程:...

[Ember Zigbee]Zigbee3.0设备自定义ZCL数据包发送数据_DesiLuo的博客-程序员秘密

针对Simplicity Studio 4,EmberZNet协议栈Zigbee3.0设备入网流程Platform:Simplicity Studio 4、EmberZNet SDK 6.4.1.0【SPP】Content:Zigbee3.0设备自定义ZCL数据包发送数据1、借助zcl-cli.c文件定义的一些变量,构建ZCL数据buffer,在自己的c文件下引用以下几个变量...

Solidity-搭建僵尸工厂_solidity 工厂_「已注销」的博客-程序员秘密

原教程:https://cryptozombies.io/zh/lesson/1第1章: 课程概述第一课你将创造一个"僵尸工厂", 用它建立一支僵尸部队。我们的工厂会把我们部队中所有的僵尸保存到数据库中工厂会有一个函数能产生新的僵尸每个僵尸会有一个随机的独一无二的面孔在后面的课程里,我们会增加功能。比如,让僵尸能攻击人类或其它僵尸! 但是在实现这些好玩的功能之前,我们先要实现创建僵尸这样的基本功能。僵尸DNA如何运作僵尸的面孔取决于它的DNA。它的DNA很简单,由一个16位的整数组成:83

<zz>开发者版本:你属于哪个版本的程序员?_iteye_7177的博客-程序员秘密

http://news.csdn.net/n/20080625/116964.html国外开发者博客中有一篇有趣的文章,将程序员按水平像软件版本号那样划分为不同的版本。相对于在招聘时分为初级,中级,高级程序员,直接表明需要某种语言N版本的程序员或许更方便直接。根据作者的观点,可将WEB开发者大致分为以下几个版本:Alpha:阅读过一些专业书籍,大多数能用Dreamweaver或者F...

推荐文章

热门文章

相关标签