001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.multipart.commons; 018 019import java.util.List; 020 021import javax.servlet.ServletContext; 022import javax.servlet.http.HttpServletRequest; 023 024import org.apache.commons.fileupload.FileItem; 025import org.apache.commons.fileupload.FileItemFactory; 026import org.apache.commons.fileupload.FileUpload; 027import org.apache.commons.fileupload.FileUploadBase; 028import org.apache.commons.fileupload.FileUploadException; 029import org.apache.commons.fileupload.servlet.ServletFileUpload; 030 031import org.springframework.util.Assert; 032import org.springframework.web.context.ServletContextAware; 033import org.springframework.web.multipart.MaxUploadSizeExceededException; 034import org.springframework.web.multipart.MultipartException; 035import org.springframework.web.multipart.MultipartHttpServletRequest; 036import org.springframework.web.multipart.MultipartResolver; 037import org.springframework.web.multipart.support.AbstractMultipartHttpServletRequest; 038import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest; 039import org.springframework.web.util.WebUtils; 040 041/** 042 * Servlet-based {@link MultipartResolver} implementation for 043 * <a href="https://commons.apache.org/proper/commons-fileupload">Apache Commons FileUpload</a> 044 * 1.2 or above. 045 * 046 * <p>Provides "maxUploadSize", "maxInMemorySize" and "defaultEncoding" settings as 047 * bean properties (inherited from {@link CommonsFileUploadSupport}). See corresponding 048 * ServletFileUpload / DiskFileItemFactory properties ("sizeMax", "sizeThreshold", 049 * "headerEncoding") for details in terms of defaults and accepted values. 050 * 051 * <p>Saves temporary files to the servlet container's temporary directory. 052 * Needs to be initialized <i>either</i> by an application context <i>or</i> 053 * via the constructor that takes a ServletContext (for standalone usage). 054 * 055 * @author Trevor D. Cook 056 * @author Juergen Hoeller 057 * @since 29.09.2003 058 * @see #CommonsMultipartResolver(ServletContext) 059 * @see #setResolveLazily 060 * @see org.apache.commons.fileupload.servlet.ServletFileUpload 061 * @see org.apache.commons.fileupload.disk.DiskFileItemFactory 062 */ 063public class CommonsMultipartResolver extends CommonsFileUploadSupport 064 implements MultipartResolver, ServletContextAware { 065 066 private boolean resolveLazily = false; 067 068 069 /** 070 * Constructor for use as bean. Determines the servlet container's 071 * temporary directory via the ServletContext passed in as through the 072 * ServletContextAware interface (typically by a WebApplicationContext). 073 * @see #setServletContext 074 * @see org.springframework.web.context.ServletContextAware 075 * @see org.springframework.web.context.WebApplicationContext 076 */ 077 public CommonsMultipartResolver() { 078 super(); 079 } 080 081 /** 082 * Constructor for standalone usage. Determines the servlet container's 083 * temporary directory via the given ServletContext. 084 * @param servletContext the ServletContext to use 085 */ 086 public CommonsMultipartResolver(ServletContext servletContext) { 087 this(); 088 setServletContext(servletContext); 089 } 090 091 092 /** 093 * Set whether to resolve the multipart request lazily at the time of 094 * file or parameter access. 095 * <p>Default is "false", resolving the multipart elements immediately, throwing 096 * corresponding exceptions at the time of the {@link #resolveMultipart} call. 097 * Switch this to "true" for lazy multipart parsing, throwing parse exceptions 098 * once the application attempts to obtain multipart files or parameters. 099 */ 100 public void setResolveLazily(boolean resolveLazily) { 101 this.resolveLazily = resolveLazily; 102 } 103 104 /** 105 * Initialize the underlying {@code org.apache.commons.fileupload.servlet.ServletFileUpload} 106 * instance. Can be overridden to use a custom subclass, e.g. for testing purposes. 107 * @param fileItemFactory the Commons FileItemFactory to use 108 * @return the new ServletFileUpload instance 109 */ 110 @Override 111 protected FileUpload newFileUpload(FileItemFactory fileItemFactory) { 112 return new ServletFileUpload(fileItemFactory); 113 } 114 115 @Override 116 public void setServletContext(ServletContext servletContext) { 117 if (!isUploadTempDirSpecified()) { 118 getFileItemFactory().setRepository(WebUtils.getTempDir(servletContext)); 119 } 120 } 121 122 123 @Override 124 public boolean isMultipart(HttpServletRequest request) { 125 return ServletFileUpload.isMultipartContent(request); 126 } 127 128 @Override 129 public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException { 130 Assert.notNull(request, "Request must not be null"); 131 if (this.resolveLazily) { 132 return new DefaultMultipartHttpServletRequest(request) { 133 @Override 134 protected void initializeMultipart() { 135 MultipartParsingResult parsingResult = parseRequest(request); 136 setMultipartFiles(parsingResult.getMultipartFiles()); 137 setMultipartParameters(parsingResult.getMultipartParameters()); 138 setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes()); 139 } 140 }; 141 } 142 else { 143 MultipartParsingResult parsingResult = parseRequest(request); 144 return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(), 145 parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes()); 146 } 147 } 148 149 /** 150 * Parse the given servlet request, resolving its multipart elements. 151 * @param request the request to parse 152 * @return the parsing result 153 * @throws MultipartException if multipart resolution failed. 154 */ 155 protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException { 156 String encoding = determineEncoding(request); 157 FileUpload fileUpload = prepareFileUpload(encoding); 158 try { 159 List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request); 160 return parseFileItems(fileItems, encoding); 161 } 162 catch (FileUploadBase.SizeLimitExceededException ex) { 163 throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex); 164 } 165 catch (FileUploadBase.FileSizeLimitExceededException ex) { 166 throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex); 167 } 168 catch (FileUploadException ex) { 169 throw new MultipartException("Failed to parse multipart servlet request", ex); 170 } 171 } 172 173 /** 174 * Determine the encoding for the given request. 175 * Can be overridden in subclasses. 176 * <p>The default implementation checks the request encoding, 177 * falling back to the default encoding specified for this resolver. 178 * @param request current HTTP request 179 * @return the encoding for the request (never {@code null}) 180 * @see javax.servlet.ServletRequest#getCharacterEncoding 181 * @see #setDefaultEncoding 182 */ 183 protected String determineEncoding(HttpServletRequest request) { 184 String encoding = request.getCharacterEncoding(); 185 if (encoding == null) { 186 encoding = getDefaultEncoding(); 187 } 188 return encoding; 189 } 190 191 @Override 192 public void cleanupMultipart(MultipartHttpServletRequest request) { 193 if (!(request instanceof AbstractMultipartHttpServletRequest) || 194 ((AbstractMultipartHttpServletRequest) request).isResolved()) { 195 try { 196 cleanupFileItems(request.getMultiFileMap()); 197 } 198 catch (Throwable ex) { 199 logger.warn("Failed to perform multipart cleanup for servlet request", ex); 200 } 201 } 202 } 203 204}