View Javadoc

1   // ========================================================================
2   // Copyright (c) 1996-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  package org.eclipse.jetty.servlets;
14  
15  import java.io.BufferedInputStream;
16  import java.io.BufferedOutputStream;
17  import java.io.ByteArrayOutputStream;
18  import java.io.File;
19  import java.io.FileOutputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.io.UnsupportedEncodingException;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.Enumeration;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  
32  import javax.servlet.Filter;
33  import javax.servlet.FilterChain;
34  import javax.servlet.FilterConfig;
35  import javax.servlet.MultipartConfigElement;
36  import javax.servlet.ServletContext;
37  import javax.servlet.ServletException;
38  import javax.servlet.ServletRequest;
39  import javax.servlet.ServletResponse;
40  import javax.servlet.http.HttpServletRequest;
41  import javax.servlet.http.HttpServletRequestWrapper;
42  import javax.servlet.http.Part;
43  
44  
45  import org.eclipse.jetty.util.IO;
46  import org.eclipse.jetty.util.LazyList;
47  import org.eclipse.jetty.util.MultiMap;
48  import org.eclipse.jetty.util.MultiPartInputStream;
49  import org.eclipse.jetty.util.QuotedStringTokenizer;
50  import org.eclipse.jetty.util.StringUtil;
51  import org.eclipse.jetty.util.TypeUtil;
52  
53  /* ------------------------------------------------------------ */
54  /**
55   * Multipart Form Data Filter.
56   * <p>
57   * This class decodes the multipart/form-data stream sent by a HTML form that uses a file input
58   * item.  Any files sent are stored to a temporary file and a File object added to the request 
59   * as an attribute.  All other values are made available via the normal getParameter API and
60   * the setCharacterEncoding mechanism is respected when converting bytes to Strings.
61   * <p>
62   * If the init parameter "delete" is set to "true", any files created will be deleted when the
63   * current request returns.
64   * 
65   * Use init parameter "maxFileSize" to set the max size file that can be uploaded.
66   * 
67   * Use init parameter "maxRequestSize" to limit the size of the multipart request.
68   * 
69   */
70  public class MultiPartFilter implements Filter
71  {
72      private final static String FILES ="org.eclipse.jetty.servlet.MultiPartFilter.files";
73      private File tempdir;
74      private boolean _deleteFiles;
75      private ServletContext _context;
76      private int _fileOutputBuffer = 0;
77      private long _maxFileSize = -1L;
78      private long _maxRequestSize = -1L;
79  
80      /* ------------------------------------------------------------------------------- */
81      /**
82       * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
83       */
84      public void init(FilterConfig filterConfig) throws ServletException
85      {
86          tempdir=(File)filterConfig.getServletContext().getAttribute("javax.servlet.context.tempdir");
87          _deleteFiles="true".equals(filterConfig.getInitParameter("deleteFiles"));
88          String fileOutputBuffer = filterConfig.getInitParameter("fileOutputBuffer");
89          if(fileOutputBuffer!=null)
90              _fileOutputBuffer = Integer.parseInt(fileOutputBuffer);
91          String maxFileSize = filterConfig.getInitParameter("maxFileSize");
92          if (maxFileSize != null)
93              _maxFileSize = Long.parseLong(maxFileSize.trim());
94          String maxRequestSize = filterConfig.getInitParameter("maxRequestSize");
95          if (maxRequestSize != null)
96              _maxRequestSize = Long.parseLong(maxRequestSize.trim());
97          
98          _context=filterConfig.getServletContext();
99      }
100 
101     /* ------------------------------------------------------------------------------- */
102     /**
103      * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
104      *      javax.servlet.ServletResponse, javax.servlet.FilterChain)
105      */
106     public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) 
107         throws IOException, ServletException
108     {
109         HttpServletRequest srequest=(HttpServletRequest)request;
110         if(srequest.getContentType()==null||!srequest.getContentType().startsWith("multipart/form-data"))
111         {
112             chain.doFilter(request,response);
113             return;
114         }
115         
116         InputStream in = new BufferedInputStream(request.getInputStream());
117         String content_type=srequest.getContentType();
118         
119         //Get current parameters so we can merge into them
120         MultiMap<String> params = new MultiMap<String>();
121         for (Iterator<Map.Entry<String,String[]>> i = request.getParameterMap().entrySet().iterator();i.hasNext();)
122         {
123             Map.Entry<String,String[]> entry=i.next();
124             Object value=entry.getValue();
125             if (value instanceof String[])
126                 params.addValues(entry.getKey(),(String[])value);
127             else
128                 params.add(entry.getKey(),value);
129         }
130         
131         MultipartConfigElement config = new MultipartConfigElement(tempdir.getCanonicalPath(), _maxFileSize, _maxRequestSize, _fileOutputBuffer);
132         MultiPartInputStream mpis = new MultiPartInputStream(in, content_type, config, tempdir);
133         
134 
135         try
136         {
137             Collection<Part> parts = mpis.getParts();
138             if (parts != null)
139             {
140                 for (Part p:parts)
141                 {
142                     MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p;
143                     if (mp.getFile() != null)
144                     {
145                         request.setAttribute(mp.getName(),mp.getFile());
146                         if (mp.getContentDispositionFilename() != null)
147                             params.add(mp.getName(), mp.getContentDispositionFilename());
148                         if (_deleteFiles)
149                         {
150                             mp.getFile().deleteOnExit();
151 
152                             ArrayList files = (ArrayList)request.getAttribute(FILES);
153                             if (files==null)
154                             {
155                                 files=new ArrayList();
156                                 request.setAttribute(FILES,files);
157                             }
158                             files.add(mp.getFile());
159                         }
160                     }
161                     else
162                     {
163                         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
164                         IO.copy(p.getInputStream(), bytes);
165                         params.add(p.getName(), bytes.toByteArray());
166                     }
167                 }
168             }
169 
170             // handle request
171             chain.doFilter(new Wrapper(srequest,params),response);
172         }
173         finally
174         {
175             deleteFiles(request);
176         }
177     }
178 
179     private void deleteFiles(ServletRequest request)
180     {
181         ArrayList files = (ArrayList)request.getAttribute(FILES);
182         if (files!=null)
183         {
184             Iterator iter = files.iterator();
185             while (iter.hasNext())
186             {
187                 File file=(File)iter.next();
188                 try
189                 {
190                     file.delete();
191                 }
192                 catch(Exception e)
193                 {
194                     _context.log("failed to delete "+file,e);
195                 }
196             }
197         }
198     }
199     
200     /* ------------------------------------------------------------ */
201     private String value(String nameEqualsValue)
202     {
203         return nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
204     }
205 
206     /* ------------------------------------------------------------------------------- */
207     /**
208      * @see javax.servlet.Filter#destroy()
209      */
210     public void destroy()
211     {
212     }
213 
214     /* ------------------------------------------------------------------------------- */
215     /* ------------------------------------------------------------------------------- */
216     private static class Wrapper extends HttpServletRequestWrapper
217     {
218         String _encoding=StringUtil.__UTF8;
219         MultiMap _params;
220         
221         /* ------------------------------------------------------------------------------- */
222         /** Constructor.
223          * @param request
224          */
225         public Wrapper(HttpServletRequest request, MultiMap map)
226         {
227             super(request);
228             this._params=map;
229         }
230         
231         /* ------------------------------------------------------------------------------- */
232         /**
233          * @see javax.servlet.ServletRequest#getContentLength()
234          */
235         @Override
236         public int getContentLength()
237         {
238             return 0;
239         }
240         
241         /* ------------------------------------------------------------------------------- */
242         /**
243          * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
244          */
245         @Override
246         public String getParameter(String name)
247         {
248             Object o=_params.get(name);
249             if (!(o instanceof byte[]) && LazyList.size(o)>0)
250                 o=LazyList.get(o,0);
251             
252             if (o instanceof byte[])
253             {
254                 try
255                 {
256                     String s=new String((byte[])o,_encoding);
257                     return s;
258                 }
259                 catch(Exception e)
260                 {
261                     e.printStackTrace();
262                 }
263             }
264             else if (o!=null)
265                 return String.valueOf(o);
266             return null;
267         }
268         
269         /* ------------------------------------------------------------------------------- */
270         /**
271          * @see javax.servlet.ServletRequest#getParameterMap()
272          */
273         @Override
274         public Map getParameterMap()
275         {
276             return Collections.unmodifiableMap(_params.toStringArrayMap());
277         }
278         
279         /* ------------------------------------------------------------------------------- */
280         /**
281          * @see javax.servlet.ServletRequest#getParameterNames()
282          */
283         @Override
284         public Enumeration getParameterNames()
285         {
286             return Collections.enumeration(_params.keySet());
287         }
288         
289         /* ------------------------------------------------------------------------------- */
290         /**
291          * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
292          */
293         @Override
294         public String[] getParameterValues(String name)
295         {
296             List l=_params.getValues(name);
297             if (l==null || l.size()==0)
298                 return new String[0];
299             String[] v = new String[l.size()];
300             for (int i=0;i<l.size();i++)
301             {
302                 Object o=l.get(i);
303                 if (o instanceof byte[])
304                 {
305                     try
306                     {
307                         v[i]=new String((byte[])o,_encoding);
308                     }
309                     catch(Exception e)
310                     {
311                         e.printStackTrace();
312                     }
313                 }
314                 else if (o instanceof String)
315                     v[i]=(String)o;
316             }
317             return v;
318         }
319         
320         /* ------------------------------------------------------------------------------- */
321         /**
322          * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)
323          */
324         @Override
325         public void setCharacterEncoding(String enc) 
326             throws UnsupportedEncodingException
327         {
328             _encoding=enc;
329         }
330     }
331 }