PageRenderTime 84ms CodeModel.GetById 40ms app.highlight 18ms RepoModel.GetById 23ms app.codeStats 0ms

/src/main/java/org/gmr/web/multipart/GFileUploadSupport.java

http://gmultipart.googlecode.com/
Java | 303 lines | 129 code | 30 blank | 144 comment | 19 complexity | ea62d545e6942a021f391ae22d7ea910 MD5 | raw file
  1/*
  2 * Copyright 2002-2008 the original author or authors.
  3 *
  4 * Licensed under the Apache License, Version 2.0 (the "License");
  5 * you may not use this file except in compliance with the License.
  6 * You may obtain a copy of the License at
  7 *
  8 *      http://www.apache.org/licenses/LICENSE-2.0
  9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed under the License is distributed on an "AS IS" BASIS,
 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 * See the License for the specific language governing permissions and
 14 * limitations under the License.
 15 */
 16
 17package org.gmr.web.multipart;
 18
 19import java.io.UnsupportedEncodingException;
 20import java.util.Collection;
 21import java.util.HashMap;
 22import java.util.List;
 23import java.util.Map;
 24
 25import org.apache.commons.fileupload.FileItem;
 26import org.apache.commons.fileupload.FileItemFactory;
 27import org.apache.commons.fileupload.FileUpload;
 28import org.apache.commons.logging.Log;
 29import org.apache.commons.logging.LogFactory;
 30import org.springframework.util.LinkedMultiValueMap;
 31import org.springframework.util.MultiValueMap;
 32import org.springframework.util.StringUtils;
 33import org.springframework.web.multipart.MultipartFile;
 34import org.springframework.web.util.WebUtils;
 35
 36/**
 37 * <p>
 38 * Important Note: This src is modifed version of {@link org.springframework.web.multipart.CommonsFileUploadSupport} to
 39 * make it work in GAE.
 40 *
 41 * Base class for multipart resolvers that use Jakarta Commons FileUpload 1.1 or higher.
 42 *
 43 * <p>
 44 * Provides common configuration properties and parsing functionality for multipart requests, using a Map of Spring
 45 * GMultipartFile instances as representation of uploaded files and a String-based parameter Map as representation of
 46 * uploaded form fields.
 47 *
 48 * <p>
 49 * Subclasses implement concrete resolution strategies for Servlet environments: see GMultipartResolver. This base class
 50 * is not tied to those APIs, factoring out common functionality.
 51 *
 52 * @author kernel164
 53 * @author Juergen Hoeller
 54 * @see GMultipartFile
 55 * @see GMultipartResolver
 56 */
 57public abstract class GFileUploadSupport {
 58
 59	protected final Log logger = LogFactory.getLog(getClass());
 60
 61	private final GFileItemFactory fileItemFactory;
 62
 63	private final FileUpload fileUpload;
 64
 65	/**
 66	 * Instantiate a new GFileUploadSupport with its corresponding FileItemFactory and FileUpload instances.
 67	 *
 68	 * @see #newFileItemFactory
 69	 * @see #newFileUpload
 70	 */
 71	public GFileUploadSupport() {
 72		this.fileItemFactory = newFileItemFactory();
 73		this.fileUpload = newFileUpload(getFileItemFactory());
 74	}
 75
 76	/**
 77	 * Return the underlying <code>GFileItemFactory</code> instance. There is
 78	 * hardly any need to access this.
 79	 *
 80	 * @return the underlying GFileItemFactory instance
 81	 */
 82	public GFileItemFactory getFileItemFactory() {
 83		return this.fileItemFactory;
 84	}
 85
 86	/**
 87	 * Return the underlying <code>org.apache.commons.fileupload.FileUpload</code> instance. There is hardly any need to
 88	 * access this.
 89	 *
 90	 * @return the underlying FileUpload instance
 91	 */
 92	public FileUpload getFileUpload() {
 93		return this.fileUpload;
 94	}
 95
 96	/**
 97	 * Set the maximum allowed size (in bytes) before uploads are refused. -1 indicates no limit (the default).
 98	 * Sets the maximum in memory thresold limit to max upload size.
 99	 *
100	 * @param maxUploadSize the maximum upload size allowed
101	 * @see org.apache.commons.fileupload.FileUploadBase#setSizeMax
102	 * @see org.apache.commons.fileupload.FileItemFactory#setSizeThreshold
103	 */
104	public void setMaxUploadSize(int maxUploadSize) {
105		this.fileUpload.setSizeMax(maxUploadSize);
106		this.fileItemFactory.setSizeThreshold(maxUploadSize);
107	}
108
109	/**
110	 * Set the default character encoding to use for parsing requests, to be applied to headers of individual parts and
111	 * to form fields. Default is ISO-8859-1, according to the Servlet spec.
112	 * <p>
113	 * If the request specifies a character encoding itself, the request encoding will override this setting. This also
114	 * allows for generically overriding the character encoding in a filter that invokes the
115	 * <code>ServletRequest.setCharacterEncoding</code> method.
116	 *
117	 * @param defaultEncoding the character encoding to use
118	 * @see javax.servlet.ServletRequest#getCharacterEncoding
119	 * @see javax.servlet.ServletRequest#setCharacterEncoding
120	 * @see WebUtils#DEFAULT_CHARACTER_ENCODING
121	 * @see org.apache.commons.fileupload.FileUploadBase#setHeaderEncoding
122	 */
123	public void setDefaultEncoding(String defaultEncoding) {
124		this.fileUpload.setHeaderEncoding(defaultEncoding);
125	}
126
127	/**
128	 * Returns the default encoding.
129	 *
130	 * @return the default encoding.
131	 */
132	protected String getDefaultEncoding() {
133		String encoding = getFileUpload().getHeaderEncoding();
134		if (encoding == null) {
135			encoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
136		}
137		return encoding;
138	}
139
140	/**
141	 * Factory method for a Commons GFileItemFactory instance.
142	 *
143	 * <p> Default implementation returns a standard GFileItemFactory. Can be overridden to use a custom subclass, e.g.
144	 * for testing purposes.
145	 *
146	 * @return the new DiskFileItemFactory instance
147	 */
148	protected GFileItemFactory newFileItemFactory() {
149		return new GFileItemFactory();
150	}
151
152	/**
153	 * Factory method for a Commons FileUpload instance.
154	 * <p>
155	 * <b>To be implemented by subclasses.</b>
156	 *
157	 * @param fileItemFactory the Commons FileItemFactory to build upon
158	 * @return the Commons FileUpload instance
159	 */
160	protected abstract FileUpload newFileUpload(FileItemFactory fileItemFactory);
161
162	/**
163	 * Determine an appropriate FileUpload instance for the given encoding.
164	 * <p>
165	 * Default implementation returns the shared FileUpload instance if the encoding matches, else creates a new
166	 * FileUpload instance with the same configuration other than the desired encoding.
167	 *
168	 * @param encoding the character encoding to use
169	 * @return an appropriate FileUpload instance.
170	 */
171	protected FileUpload prepareFileUpload(String encoding) {
172		FileUpload fileUpload = getFileUpload();
173		FileUpload actualFileUpload = fileUpload;
174
175		// Use new temporary FileUpload instance if the request specifies
176		// its own encoding that does not match the default encoding.
177		if (encoding != null && !encoding.equals(fileUpload.getHeaderEncoding())) {
178			actualFileUpload = newFileUpload(getFileItemFactory());
179			actualFileUpload.setSizeMax(fileUpload.getSizeMax());
180			actualFileUpload.setHeaderEncoding(encoding);
181		}
182
183		return actualFileUpload;
184	}
185
186	/**
187	 * Parse the given List of Commons FileItems into a Spring MultipartParsingResult, containing Spring MultipartFile
188	 * instances and a Map of multipart parameter.
189	 *
190	 * @param fileItems the Commons FileIterms to parse
191	 * @param encoding the encoding to use for form fields
192	 * @return the Spring MultipartParsingResult
193	 * @see GMultipartFile#CommonsMultipartFile(org.apache.commons.fileupload.FileItem)
194	 */
195	protected MultipartParsingResult parseFileItems(List<FileItem> fileItems, String encoding) {
196		MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap<String, MultipartFile>();
197		Map<String, String[]> multipartParameters = new HashMap<String, String[]>();
198		Map<String, String> multipartParameterContentTypes = new HashMap<String, String>();
199
200		// Extract multipart files and multipart parameters.
201		for (FileItem fileItem : fileItems) {
202			if (fileItem.isFormField()) {
203				String value = null;
204				if (encoding != null) {
205					try {
206						value = fileItem.getString(encoding);
207					} catch (UnsupportedEncodingException ex) {
208						if (logger.isWarnEnabled()) {
209							logger.warn("Could not decode multipart item '" + fileItem.getFieldName() + "' with encoding '" + encoding + "': using platform default");
210						}
211						value = fileItem.getString();
212					}
213				} else {
214					value = fileItem.getString();
215				}
216				String[] curParam = multipartParameters.get(fileItem.getFieldName());
217				if (curParam == null) {
218					// simple form field
219					multipartParameters.put(fileItem.getFieldName(), new String[] { value });
220				} else {
221					// array of simple form fields
222					String[] newParam = StringUtils.addStringToArray(curParam, value);
223					multipartParameters.put(fileItem.getFieldName(), newParam);
224				}
225				multipartParameterContentTypes.put(fileItem.getFieldName(), fileItem.getContentType());
226			} else {
227				// multipart file field
228				GMultipartFile file = new GMultipartFile(fileItem);
229				multipartFiles.add(file.getName(), file);
230				if (logger.isDebugEnabled()) {
231					logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize() +
232							" bytes with original filename [" + file.getOriginalFilename() + "], stored " +
233							file.getStorageDescription());
234				}
235			}
236		}
237		return new MultipartParsingResult(multipartFiles, multipartParameters, multipartParameterContentTypes);
238	}
239
240	/**
241	 * Cleanup the Spring MultipartFiles created during multipart parsing.
242	 * <p>
243	 * Deletes the underlying Commons FileItem instances.
244	 *
245	 * @param multipartFiles Collection of MultipartFile instances
246	 * @see org.apache.commons.fileupload.FileItem#delete()
247	 */
248	protected void cleanupFileItems(Collection<MultipartFile> multipartFiles) {
249		for (MultipartFile file : multipartFiles) {
250			if (file instanceof GMultipartFile) {
251				GMultipartFile cmf = (GMultipartFile) file;
252				cmf.getFileItem().delete();
253				if (logger.isDebugEnabled()) {
254					logger.debug("Cleaning up multipart file [" + cmf.getName() + "] with original filename [" + cmf.getOriginalFilename() + "], stored " + cmf.getStorageDescription());
255				}
256			}
257		}
258	}
259
260	/**
261	 * Holder for a Map of Spring MultipartFiles and a Map of multipart parameters.
262	 */
263	protected static class MultipartParsingResult {
264
265		private final MultiValueMap<String, MultipartFile> multipartFiles;
266
267		private final Map<String, String[]> multipartParameters;
268
269		private final Map<String, String> multipartParameterContentTypes;
270
271		/**
272		 * Create a new MultipartParsingResult.
273		 *
274		 * @param mpFiles Map of field name to MultipartFile instance
275		 * @param mpParams Map of field name to form field String value
276		 */
277		public MultipartParsingResult(MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams,
278                                      Map<String, String> mpParamContentTypes) {
279			this.multipartFiles = mpFiles;
280			this.multipartParameters = mpParams;
281            this.multipartParameterContentTypes = mpParamContentTypes;
282		}
283
284		/**
285		 * Return the multipart files as Map of field name to MultipartFile instance.
286		 */
287		public MultiValueMap<String, MultipartFile> getMultipartFiles() {
288			return this.multipartFiles;
289		}
290
291		/**
292		 * Return the multipart parameters as Map of field name to form field String value.
293		 */
294		public Map<String, String[]> getMultipartParameters() {
295			return this.multipartParameters;
296		}
297
298		public Map<String, String> getMultipartParameterContentTypes() {
299			return this.multipartParameterContentTypes;
300		}
301	}
302
303}