View Javadoc

1   /*
2    * $Id: JakartaMultiPartRequest.java 724074 2008-12-07 04:51:34Z wesw $
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  public class JakartaMultiPartRequest implements MultiPartRequest {
52      
53      static final Logger LOG = LoggerFactory.getLogger(MultiPartRequest.class);
54      
55      // maps parameter name -> List of FileItem objects
56      private final Map<String,List<FileItem>> files = new HashMap<String,List<FileItem>>();
57  
58      // maps parameter name -> List of param values
59      private final Map<String,List<String>> params = new HashMap<String,List<String>>();
60  
61      // any errors while processing this request
62      private final List<String> errors = new ArrayList<String>();
63      
64      private long maxSize;
65      
66      @Inject(StrutsConstants.STRUTS_MULTIPART_MAXSIZE)
67      public void setMaxSize(String maxSize) {
68          this.maxSize = Long.parseLong(maxSize);
69      }
70  
71      /***
72       * Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell's
73       * multipart classes (see class description).
74       *
75       * @param saveDir        the directory to save off the file
76       * @param servletRequest the request containing the multipart
77       * @throws java.io.IOException  is thrown if encoding fails.
78       */
79      public void parse(HttpServletRequest servletRequest, String saveDir)
80              throws IOException {
81          DiskFileItemFactory fac = new DiskFileItemFactory();
82          // Make sure that the data is written to file
83          fac.setSizeThreshold(0);
84          if (saveDir != null) {
85              fac.setRepository(new File(saveDir));
86          }
87  
88          // Parse the request
89          try {
90              ServletFileUpload upload = new ServletFileUpload(fac);
91              upload.setSizeMax(maxSize);
92  
93              List items = upload.parseRequest(createRequestContext(servletRequest));
94  
95              for (Object item1 : items) {
96                  FileItem item = (FileItem) item1;
97                  if (LOG.isDebugEnabled()) LOG.debug("Found item " + item.getFieldName());
98                  if (item.isFormField()) {
99                      LOG.debug("Item is a normal form field");
100                     List<String> values;
101                     if (params.get(item.getFieldName()) != null) {
102                         values = params.get(item.getFieldName());
103                     } else {
104                         values = new ArrayList<String>();
105                     }
106 
107                     // note: see http://jira.opensymphony.com/browse/WW-633
108                     // basically, in some cases the charset may be null, so
109                     // we're just going to try to "other" method (no idea if this
110                     // will work)
111                     String charset = servletRequest.getCharacterEncoding();
112                     if (charset != null) {
113                         values.add(item.getString(charset));
114                     } else {
115                         values.add(item.getString());
116                     }
117                     params.put(item.getFieldName(), values);
118                 } else {
119                     LOG.debug("Item is a file upload");
120 
121                     // Skip file uploads that don't have a file name - meaning that no file was selected.
122                     if (item.getName() == null || item.getName().trim().length() < 1) {
123                         LOG.debug("No file has been uploaded for the field: " + item.getFieldName());
124                         continue;
125                     }
126 
127                     List<FileItem> values;
128                     if (files.get(item.getFieldName()) != null) {
129                         values = files.get(item.getFieldName());
130                     } else {
131                         values = new ArrayList<FileItem>();
132                     }
133 
134                     values.add(item);
135                     files.put(item.getFieldName(), values);
136                 }
137             }
138         } catch (FileUploadException e) {
139             LOG.error("Unable to parse request", e);
140             errors.add(e.getMessage());
141         }
142     }
143 
144     /* (non-Javadoc)
145      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileParameterNames()
146      */
147     public Enumeration<String> getFileParameterNames() {
148         return Collections.enumeration(files.keySet());
149     }
150 
151     /* (non-Javadoc)
152      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getContentType(java.lang.String)
153      */
154     public String[] getContentType(String fieldName) {
155         List<FileItem> items = files.get(fieldName);
156 
157         if (items == null) {
158             return null;
159         }
160 
161         List<String> contentTypes = new ArrayList<String>(items.size());
162         for (FileItem fileItem : items) {
163             contentTypes.add(fileItem.getContentType());
164         }
165 
166         return 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<FileItem> items = files.get(fieldName);
174 
175         if (items == null) {
176             return null;
177         }
178 
179         List<File> fileList = new ArrayList<File>(items.size());
180         for (FileItem fileItem : items) {
181             fileList.add(((DiskFileItem) fileItem).getStoreLocation());
182         }
183 
184         return fileList.toArray(new File[fileList.size()]);
185     }
186 
187     /* (non-Javadoc)
188      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileNames(java.lang.String)
189      */
190     public String[] getFileNames(String fieldName) {
191         List<FileItem> items = files.get(fieldName);
192 
193         if (items == null) {
194             return null;
195         }
196 
197         List<String> fileNames = new ArrayList<String>(items.size());
198         for (FileItem fileItem : items) {
199             fileNames.add(getCanonicalName(fileItem.getName()));
200         }
201 
202         return fileNames.toArray(new String[fileNames.size()]);
203     }
204 
205     /* (non-Javadoc)
206      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFilesystemName(java.lang.String)
207      */
208     public String[] getFilesystemName(String fieldName) {
209         List<FileItem> items = files.get(fieldName);
210 
211         if (items == null) {
212             return null;
213         }
214 
215         List<String> fileNames = new ArrayList<String>(items.size());
216         for (FileItem fileItem : items) {
217             fileNames.add(((DiskFileItem) fileItem).getStoreLocation().getName());
218         }
219 
220         return fileNames.toArray(new String[fileNames.size()]);
221     }
222 
223     /* (non-Javadoc)
224      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameter(java.lang.String)
225      */
226     public String getParameter(String name) {
227         List<String> v = params.get(name);
228         if (v != null && v.size() > 0) {
229             return v.get(0);
230         }
231 
232         return null;
233     }
234 
235     /* (non-Javadoc)
236      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterNames()
237      */
238     public Enumeration<String> getParameterNames() {
239         return Collections.enumeration(params.keySet());
240     }
241 
242     /* (non-Javadoc)
243      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterValues(java.lang.String)
244      */
245     public String[] getParameterValues(String name) {
246         List<String> v = params.get(name);
247         if (v != null && v.size() > 0) {
248             return v.toArray(new String[v.size()]);
249         }
250 
251         return null;
252     }
253 
254     /* (non-Javadoc)
255      * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getErrors()
256      */
257     public List getErrors() {
258         return errors;
259     }
260 
261     /***
262      * Returns the canonical name of the given file.
263      *
264      * @param filename  the given file
265      * @return the canonical name of the given file
266      */
267     private String getCanonicalName(String filename) {
268         int forwardSlash = filename.lastIndexOf("/");
269         int backwardSlash = filename.lastIndexOf("//");
270         if (forwardSlash != -1 && forwardSlash > backwardSlash) {
271             filename = filename.substring(forwardSlash + 1, filename.length());
272         } else if (backwardSlash != -1 && backwardSlash >= forwardSlash) {
273             filename = filename.substring(backwardSlash + 1, filename.length());
274         }
275 
276         return filename;
277     }
278 
279     /***
280      * Creates a RequestContext needed by Jakarta Commons Upload.
281      *
282      * @param req  the request.
283      * @return a new request context.
284      */
285     private RequestContext createRequestContext(final HttpServletRequest req) {
286         return new RequestContext() {
287             public String getCharacterEncoding() {
288                 return req.getCharacterEncoding();
289             }
290 
291             public String getContentType() {
292                 return req.getContentType();
293             }
294 
295             public int getContentLength() {
296                 return req.getContentLength();
297             }
298 
299             public InputStream getInputStream() throws IOException {
300                 InputStream in = req.getInputStream();
301                 if (in == null) {
302                     throw new IOException("Missing content in the request");
303                 }
304                 return req.getInputStream();
305             }
306         };
307     }
308 
309 }