View Javadoc

1   /*
2    * $Id: JakartaMultiPartRequest.java 651946 2008-04-27 13:41:38Z apetrelli $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.struts2.dispatcher.multipart;
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.Enumeration;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  
34  import javax.servlet.http.HttpServletRequest;
35  
36  import org.apache.commons.fileupload.FileItem;
37  import org.apache.commons.fileupload.FileUploadException;
38  import org.apache.commons.fileupload.RequestContext;
39  import org.apache.commons.fileupload.disk.DiskFileItem;
40  import org.apache.commons.fileupload.disk.DiskFileItemFactory;
41  import org.apache.commons.fileupload.servlet.ServletFileUpload;
42  import org.apache.struts2.StrutsConstants;
43  
44  import com.opensymphony.xwork2.inject.Inject;
45  import com.opensymphony.xwork2.util.logging.Logger;
46  import com.opensymphony.xwork2.util.logging.LoggerFactory;
47  
48  /***
49   * Multipart form data request adapter for Jakarta Commons Fileupload package.
50   *
51   */
52  public class JakartaMultiPartRequest implements MultiPartRequest {
53      
54      static final Logger LOG = LoggerFactory.getLogger(MultiPartRequest.class);
55      
56      // maps parameter name -> List of FileItem objects
57      private Map<String,List<FileItem>> files = new HashMap<String,List<FileItem>>();
58      // maps parameter name -> List of param values
59      private Map<String,List<String>> params = new HashMap<String,List<String>>();
60      // any errors while processing this request
61      private List<String> errors = new ArrayList<String>();
62      
63      private long maxSize;
64      
65      @Inject(StrutsConstants.STRUTS_MULTIPART_MAXSIZE)
66      public void setMaxSize(String maxSize) {
67          this.maxSize = Long.parseLong(maxSize);
68      }
69  
70      /***
71       * Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell's
72       * multipart classes (see class description).
73       *
74       * @param saveDir        the directory to save off the file
75       * @param servletRequest the request containing the multipart
76       * @throws java.io.IOException  is thrown if encoding fails.
77       */
78      public void parse(HttpServletRequest servletRequest, String saveDir)
79              throws IOException {
80          DiskFileItemFactory fac = new DiskFileItemFactory();
81          // Make sure that the data is written to file
82          fac.setSizeThreshold(0);
83          if (saveDir != null) {
84              fac.setRepository(new File(saveDir));
85          }
86  
87          // Parse the request
88          try {
89              ServletFileUpload upload = new ServletFileUpload(fac);
90              upload.setSizeMax(maxSize);
91  
92              List items = upload.parseRequest(createRequestContext(servletRequest));
93  
94              for (Object item1 : items) {
95                  FileItem item = (FileItem) item1;
96                  if (LOG.isDebugEnabled()) LOG.debug("Found item " + item.getFieldName());
97                  if (item.isFormField()) {
98                      LOG.debug("Item is a normal form field");
99                      List<String> values;
100                     if (params.get(item.getFieldName()) != null) {
101                         values = params.get(item.getFieldName());
102                     } else {
103                         values = new ArrayList<String>();
104                     }
105 
106                     // note: see http://jira.opensymphony.com/browse/WW-633
107                     // basically, in some cases the charset may be null, so
108                     // we're just going to try to "other" method (no idea if this
109                     // will work)
110                     String charset = servletRequest.getCharacterEncoding();
111                     if (charset != null) {
112                         values.add(item.getString(charset));
113                     } else {
114                         values.add(item.getString());
115                     }
116                     params.put(item.getFieldName(), values);
117                 } else {
118                     LOG.debug("Item is a file upload");
119 
120                     // Skip file uploads that don't have a file name - meaning that no file was selected.
121                     if (item.getName() == null || item.getName().trim().length() < 1) {
122                         LOG.debug("No file has been uploaded for the field: " + item.getFieldName());
123                         continue;
124                     }
125 
126                     List<FileItem> values;
127                     if (files.get(item.getFieldName()) != null) {
128                         values = files.get(item.getFieldName());
129                     } else {
130                         values = new ArrayList<FileItem>();
131                     }
132 
133                     values.add(item);
134                     files.put(item.getFieldName(), values);
135                 }
136             }
137         } catch (FileUploadException e) {
138             LOG.error("Unable to parse request", e);
139             errors.add(e.getMessage());
140         }
141     }
142 
143     /* (non-Javadoc)
144      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileParameterNames()
145      */
146     public Enumeration<String> getFileParameterNames() {
147         return Collections.enumeration(files.keySet());
148     }
149 
150     /* (non-Javadoc)
151      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getContentType(java.lang.String)
152      */
153     public String[] getContentType(String fieldName) {
154         List items = (List) files.get(fieldName);
155 
156         if (items == null) {
157             return null;
158         }
159 
160         List<String> contentTypes = new ArrayList<String>(items.size());
161         for (int i = 0; i < items.size(); i++) {
162             FileItem fileItem = (FileItem) items.get(i);
163             contentTypes.add(fileItem.getContentType());
164         }
165 
166         return (String[]) contentTypes.toArray(new String[contentTypes.size()]);
167     }
168 
169     /* (non-Javadoc)
170      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFile(java.lang.String)
171      */
172     public File[] getFile(String fieldName) {
173         List items = (List) files.get(fieldName);
174 
175         if (items == null) {
176             return null;
177         }
178 
179         List<File> fileList = new ArrayList<File>(items.size());
180         for (int i = 0; i < items.size(); i++) {
181             DiskFileItem fileItem = (DiskFileItem) items.get(i);
182             fileList.add(fileItem.getStoreLocation());
183         }
184 
185         return (File[]) fileList.toArray(new File[fileList.size()]);
186     }
187 
188     /* (non-Javadoc)
189      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileNames(java.lang.String)
190      */
191     public String[] getFileNames(String fieldName) {
192         List<FileItem> items = files.get(fieldName);
193 
194         if (items == null) {
195             return null;
196         }
197 
198         List<String> fileNames = new ArrayList<String>(items.size());
199         for (int i = 0; i < items.size(); i++) {
200             DiskFileItem fileItem = (DiskFileItem) items.get(i);
201             fileNames.add(getCanonicalName(fileItem.getName()));
202         }
203 
204         return (String[]) fileNames.toArray(new String[fileNames.size()]);
205     }
206 
207     /* (non-Javadoc)
208      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFilesystemName(java.lang.String)
209      */
210     public String[] getFilesystemName(String fieldName) {
211         List items = (List) files.get(fieldName);
212 
213         if (items == null) {
214             return null;
215         }
216 
217         List<String> fileNames = new ArrayList<String>(items.size());
218         for (int i = 0; i < items.size(); i++) {
219             DiskFileItem fileItem = (DiskFileItem) items.get(i);
220             fileNames.add(fileItem.getStoreLocation().getName());
221         }
222 
223         return (String[]) fileNames.toArray(new String[fileNames.size()]);
224     }
225 
226     /* (non-Javadoc)
227      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameter(java.lang.String)
228      */
229     public String getParameter(String name) {
230         List v = (List) params.get(name);
231         if (v != null && v.size() > 0) {
232             return (String) v.get(0);
233         }
234 
235         return null;
236     }
237 
238     /* (non-Javadoc)
239      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterNames()
240      */
241     public Enumeration<String> getParameterNames() {
242         return Collections.enumeration(params.keySet());
243     }
244 
245     /* (non-Javadoc)
246      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterValues(java.lang.String)
247      */
248     public String[] getParameterValues(String name) {
249         List<String> v = params.get(name);
250         if (v != null && v.size() > 0) {
251             return (String[]) v.toArray(new String[v.size()]);
252         }
253 
254         return null;
255     }
256 
257     /* (non-Javadoc)
258      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getErrors()
259      */
260     public List getErrors() {
261         return errors;
262     }
263 
264     /***
265      * Returns the canonical name of the given file.
266      *
267      * @param filename  the given file
268      * @return the canonical name of the given file
269      */
270     private String getCanonicalName(String filename) {
271         int forwardSlash = filename.lastIndexOf("/");
272         int backwardSlash = filename.lastIndexOf("//");
273         if (forwardSlash != -1 && forwardSlash > backwardSlash) {
274             filename = filename.substring(forwardSlash + 1, filename.length());
275         } else if (backwardSlash != -1 && backwardSlash >= forwardSlash) {
276             filename = filename.substring(backwardSlash + 1, filename.length());
277         }
278 
279         return filename;
280     }
281 
282     /***
283      * Creates a RequestContext needed by Jakarta Commons Upload.
284      *
285      * @param req  the request.
286      * @return a new request context.
287      */
288     private RequestContext createRequestContext(final HttpServletRequest req) {
289         return new RequestContext() {
290             public String getCharacterEncoding() {
291                 return req.getCharacterEncoding();
292             }
293 
294             public String getContentType() {
295                 return req.getContentType();
296             }
297 
298             public int getContentLength() {
299                 return req.getContentLength();
300             }
301 
302             public InputStream getInputStream() throws IOException {
303                 InputStream in = req.getInputStream();
304                 if (in == null) {
305                     throw new IOException("Missing content in the request");
306                 }
307                 return req.getInputStream();
308             }
309         };
310     }
311 
312 }