001: /*
002: * Copyright 2002-2006 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: * http://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:
017: package org.springframework.web.multipart.cos;
018:
019: import java.io.File;
020: import java.io.IOException;
021: import java.util.Enumeration;
022:
023: import javax.servlet.ServletContext;
024: import javax.servlet.http.HttpServletRequest;
025:
026: import com.oreilly.servlet.MultipartRequest;
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029:
030: import org.springframework.core.io.Resource;
031: import org.springframework.web.context.ServletContextAware;
032: import org.springframework.web.multipart.MaxUploadSizeExceededException;
033: import org.springframework.web.multipart.MultipartException;
034: import org.springframework.web.multipart.MultipartHttpServletRequest;
035: import org.springframework.web.multipart.MultipartResolver;
036: import org.springframework.web.util.WebUtils;
037:
038: /**
039: * {@link MultipartResolver} implementation for Jason Hunter's
040: * <a href="http://www.servlets.com/cos">COS (com.oreilly.servlet)</a>.
041: * Works with a COS MultipartRequest underneath.
042: *
043: * <p>Provides "maxUploadSize" and "defaultEncoding" settings as bean properties;
044: * see respective MultipartRequest constructor parameters for details.
045: * Default maximum file size is unlimited; fallback encoding is the platform's default.
046: *
047: * @author Juergen Hoeller
048: * @since 06.10.2003
049: * @see CosMultipartHttpServletRequest
050: * @see com.oreilly.servlet.MultipartRequest
051: */
052: public class CosMultipartResolver implements MultipartResolver,
053: ServletContextAware {
054:
055: /**
056: * Constant identifier for the mulipart content type : 'multipart/form-data'.
057: */
058: public static final String MULTIPART_CONTENT_TYPE = "multipart/form-data";
059:
060: /** Logger available to subclasses */
061: protected final Log logger = LogFactory.getLog(getClass());
062:
063: private int maxUploadSize = Integer.MAX_VALUE;
064:
065: private String defaultEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
066:
067: private File uploadTempDir;
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 CosMultipartResolver() {
078: }
079:
080: /**
081: * Constructor for standalone usage. Determines the servlet container's
082: * temporary directory via the given ServletContext.
083: * @param servletContext the ServletContext to use (must not be <code>null</code>)
084: * @throws IllegalArgumentException if the supplied {@link ServletContext} is <code>null</code>
085: */
086: public CosMultipartResolver(ServletContext servletContext) {
087: this .uploadTempDir = WebUtils.getTempDir(servletContext);
088: }
089:
090: /**
091: * Set the maximum allowed size (in bytes) before uploads are refused.
092: * -1 indicates no limit (the default).
093: * @param maxUploadSize the maximum file size allowed
094: */
095: public void setMaxUploadSize(int maxUploadSize) {
096: this .maxUploadSize = maxUploadSize;
097: }
098:
099: /**
100: * Return the maximum allowed size (in bytes) before uploads are refused.
101: */
102: protected int getMaxUploadSize() {
103: return maxUploadSize;
104: }
105:
106: /**
107: * Set the default character encoding to use for parsing requests,
108: * to be applied to headers of individual parts and to form fields.
109: * Default is ISO-8859-1, according to the Servlet spec.
110: * <p>If the request specifies a character encoding itself, the request
111: * encoding will override this setting. This also allows for generically
112: * overriding the character encoding in a filter that invokes the
113: * ServletRequest.setCharacterEncoding method.
114: * @param defaultEncoding the character encoding to use
115: * @see #determineEncoding
116: * @see javax.servlet.ServletRequest#getCharacterEncoding
117: * @see javax.servlet.ServletRequest#setCharacterEncoding
118: * @see WebUtils#DEFAULT_CHARACTER_ENCODING
119: */
120: public void setDefaultEncoding(String defaultEncoding) {
121: this .defaultEncoding = defaultEncoding;
122: }
123:
124: /**
125: * Return the default character encoding to use for parsing requests.
126: */
127: protected String getDefaultEncoding() {
128: return defaultEncoding;
129: }
130:
131: /**
132: * Set the temporary directory where uploaded files get stored.
133: * Default is the servlet container's temporary directory for the web application.
134: * @see org.springframework.web.util.WebUtils#TEMP_DIR_CONTEXT_ATTRIBUTE
135: */
136: public void setUploadTempDir(Resource uploadTempDir)
137: throws IOException {
138: if (!uploadTempDir.exists()
139: && !uploadTempDir.getFile().mkdirs()) {
140: throw new IllegalArgumentException("Given uploadTempDir ["
141: + uploadTempDir + "] could not be created");
142: }
143: this .uploadTempDir = uploadTempDir.getFile();
144: }
145:
146: /**
147: * Return the temporary directory where uploaded files get stored.
148: */
149: protected File getUploadTempDir() {
150: return uploadTempDir;
151: }
152:
153: public void setServletContext(ServletContext servletContext) {
154: if (this .uploadTempDir == null) {
155: this .uploadTempDir = WebUtils.getTempDir(servletContext);
156: }
157: }
158:
159: public boolean isMultipart(HttpServletRequest request) {
160: return request.getContentType() != null
161: && request.getContentType().startsWith(
162: MULTIPART_CONTENT_TYPE);
163: }
164:
165: public MultipartHttpServletRequest resolveMultipart(
166: HttpServletRequest request) throws MultipartException {
167: try {
168: MultipartRequest multipartRequest = newMultipartRequest(request);
169: if (logger.isDebugEnabled()) {
170: Enumeration fileNames = multipartRequest.getFileNames();
171: while (fileNames.hasMoreElements()) {
172: String fileName = (String) fileNames.nextElement();
173: File file = multipartRequest.getFile(fileName);
174: logger.debug("Found multipart file '"
175: + fileName
176: + "' of size "
177: + (file != null ? file.length() : 0)
178: + " bytes with original filename ["
179: + multipartRequest
180: .getOriginalFileName(fileName)
181: + "], "
182: + (file != null ? "stored at ["
183: + file.getAbsolutePath() + "]"
184: : "empty"));
185: }
186: }
187: return new CosMultipartHttpServletRequest(request,
188: multipartRequest);
189: } catch (IOException ex) {
190: // Unfortunately, COS always throws an IOException,
191: // so we need to check the error message here!
192: if (ex.getMessage().indexOf("exceeds limit") != -1) {
193: throw new MaxUploadSizeExceededException(
194: this .maxUploadSize, ex);
195: } else {
196: throw new MultipartException(
197: "Could not parse multipart request", ex);
198: }
199: }
200: }
201:
202: /**
203: * Create a com.oreilly.servlet.MultipartRequest for the given HTTP request.
204: * Can be overridden to use a custom subclass, e.g. for testing purposes.
205: * @param request current HTTP request
206: * @return the new MultipartRequest
207: * @throws IOException if thrown by the MultipartRequest constructor
208: */
209: protected MultipartRequest newMultipartRequest(
210: HttpServletRequest request) throws IOException {
211: String tempPath = this .uploadTempDir.getAbsolutePath();
212: String enc = determineEncoding(request);
213: return new MultipartRequest(request, tempPath,
214: this .maxUploadSize, enc);
215: }
216:
217: /**
218: * Determine the encoding for the given request.
219: * Can be overridden in subclasses.
220: * <p>The default implementation checks the request encoding,
221: * falling back to the default encoding specified for this resolver.
222: * @param request current HTTP request
223: * @return the encoding for the request (never <code>null</code>)
224: * @see javax.servlet.ServletRequest#getCharacterEncoding
225: * @see #setDefaultEncoding
226: */
227: protected String determineEncoding(HttpServletRequest request) {
228: String enc = request.getCharacterEncoding();
229: if (enc == null) {
230: enc = this .defaultEncoding;
231: }
232: return enc;
233: }
234:
235: public void cleanupMultipart(MultipartHttpServletRequest request) {
236: MultipartRequest multipartRequest = ((CosMultipartHttpServletRequest) request)
237: .getMultipartRequest();
238: Enumeration fileNames = multipartRequest.getFileNames();
239: while (fileNames.hasMoreElements()) {
240: String fileName = (String) fileNames.nextElement();
241: File file = multipartRequest.getFile(fileName);
242: if (file != null) {
243: if (file.exists()) {
244: if (file.delete()) {
245: if (logger.isDebugEnabled()) {
246: logger
247: .debug("Cleaned up multipart file '"
248: + fileName
249: + "' with original filename ["
250: + multipartRequest
251: .getOriginalFileName(fileName)
252: + "], stored at ["
253: + file.getAbsolutePath()
254: + "]");
255: }
256: } else {
257: logger.warn("Could not delete multipart file '"
258: + fileName
259: + "' with original filename ["
260: + multipartRequest
261: .getOriginalFileName(fileName)
262: + "], stored at ["
263: + file.getAbsolutePath() + "]");
264: }
265: } else {
266: if (logger.isDebugEnabled()) {
267: logger
268: .debug("Multipart file '"
269: + fileName
270: + "' with original filename ["
271: + multipartRequest
272: .getOriginalFileName(fileName)
273: + "] has already been moved - no cleanup necessary");
274: }
275: }
276: }
277: }
278: }
279:
280: }
|