KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > servlet > MultiPartRequest


1 // ========================================================================
2
// $Id: MultiPartRequest.java,v 1.16 2005/12/02 20:13:52 gregwilkins Exp $
3
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
4
// ------------------------------------------------------------------------
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
// ========================================================================
15

16 package org.mortbay.servlet;
17
18 import java.io.ByteArrayInputStream JavaDoc;
19 import java.io.ByteArrayOutputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.UnsupportedEncodingException JavaDoc;
23 import java.util.Hashtable JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Set JavaDoc;
26 import java.util.StringTokenizer JavaDoc;
27
28 import javax.servlet.http.HttpServletRequest JavaDoc;
29
30 import org.apache.commons.logging.Log;
31 import org.mortbay.log.LogFactory;
32 import org.mortbay.http.HttpFields;
33 import org.mortbay.util.LineInput;
34 import org.mortbay.util.MultiMap;
35 import org.mortbay.util.StringUtil;
36
37 /* ------------------------------------------------------------ */
38 /** Multipart Form Data request.
39  * <p>
40  * This class decodes the multipart/form-data stream sent by
41  * a HTML form that uses a file input item.
42  *
43  * <p><h4>Usage</h4>
44  * Each part of the form data is named from the HTML form and
45  * is available either via getString(name) or getInputStream(name).
46  * Furthermore the MIME parameters and filename can be requested for
47  * each part.
48  * <pre>
49  * </pre>
50  *
51  * @version $Id: MultiPartRequest.java,v 1.16 2005/12/02 20:13:52 gregwilkins Exp $
52  * @author Greg Wilkins
53  * @author Jim Crossley
54  */

55 public class MultiPartRequest
56 {
57     private static Log log = LogFactory.getLog(MultiPartRequest.class);
58
59     /* ------------------------------------------------------------ */
60     HttpServletRequest JavaDoc _request;
61     LineInput _in;
62     String JavaDoc _boundary;
63     String JavaDoc _encoding;
64     byte[] _byteBoundary;
65     MultiMap _partMap = new MultiMap(10);
66     int _char=-2;
67     boolean _lastPart=false;
68     
69     /* ------------------------------------------------------------ */
70     /** Constructor.
71      * @param request The request containing a multipart/form-data
72      * request
73      * @exception IOException IOException
74      */

75     public MultiPartRequest(HttpServletRequest JavaDoc request)
76         throws IOException JavaDoc
77     {
78         _request=request;
79         String JavaDoc content_type = request.getHeader(HttpFields.__ContentType);
80         if (!content_type.startsWith("multipart/form-data"))
81             throw new IOException JavaDoc("Not multipart/form-data request");
82
83         if(log.isDebugEnabled())log.debug("Multipart content type = "+content_type);
84         _encoding = request.getCharacterEncoding();
85         if (_encoding != null)
86             _in = new LineInput(request.getInputStream(), 2048, _encoding);
87         else
88             _in = new LineInput(request.getInputStream());
89         
90         // Extract boundary string
91
_boundary="--"+
92             value(content_type.substring(content_type.indexOf("boundary=")));
93         
94         if(log.isDebugEnabled())log.debug("Boundary="+_boundary);
95         _byteBoundary= (_boundary+"--").getBytes(StringUtil.__ISO_8859_1);
96         
97         loadAllParts();
98     }
99     
100     /* ------------------------------------------------------------ */
101     /** Get the part names.
102      * @return an array of part names
103      */

104     public String JavaDoc[] getPartNames()
105     {
106         Set JavaDoc s = _partMap.keySet();
107         return (String JavaDoc[]) s.toArray(new String JavaDoc[s.size()]);
108     }
109     
110     /* ------------------------------------------------------------ */
111     /** Check if a named part is present
112      * @param name The part
113      * @return true if it was included
114      */

115     public boolean contains(String JavaDoc name)
116     {
117         Part part = (Part)_partMap.get(name);
118         return (part!=null);
119     }
120     
121     /* ------------------------------------------------------------ */
122     /** Get the data of a part as a string.
123      * @param name The part name
124      * @return The part data
125      */

126     public String JavaDoc getString(String JavaDoc name)
127     {
128         List JavaDoc part = _partMap.getValues(name);
129         if (part==null)
130             return null;
131         if (_encoding != null)
132         {
133             try
134             {
135                 return new String JavaDoc(((Part)part.get(0))._data, _encoding);
136             }
137             catch (UnsupportedEncodingException JavaDoc uee)
138             {
139                 if (log.isDebugEnabled())log.debug("Invalid character set: " + uee);
140                 return null;
141             }
142         }
143         else
144             return new String JavaDoc(((Part)part.get(0))._data);
145     }
146     
147     /* ------------------------------------------------------------ */
148     /**
149      * @param name The part name
150      * @return The parts data
151      */

152     public String JavaDoc[] getStrings(String JavaDoc name)
153     {
154         List JavaDoc parts = _partMap.getValues(name);
155         if (parts==null)
156             return null;
157         String JavaDoc[] strings = new String JavaDoc[parts.size()];
158         
159         if (_encoding == null)
160         {
161             for (int i=0; i<strings.length; i++)
162                 strings[i] = new String JavaDoc(((Part)parts.get(i))._data);
163         }
164         else
165         {
166             try
167             {
168                 for (int i=0; i<strings.length; i++)
169                     strings[i] = new String JavaDoc(((Part)parts.get(i))._data, _encoding);
170             }
171             catch (UnsupportedEncodingException JavaDoc uee)
172             {
173                 if (log.isDebugEnabled())log.debug("Invalid character set: " + uee);
174                 return null;
175             }
176         }
177        
178         return strings;
179     }
180     
181     /* ------------------------------------------------------------ */
182     /** Get the data of a part as a stream.
183      * @param name The part name
184      * @return Stream providing the part data
185      */

186     public InputStream JavaDoc getInputStream(String JavaDoc name)
187     {
188         List JavaDoc part = (List JavaDoc)_partMap.getValues(name);
189         if (part==null)
190             return null;
191         return new ByteArrayInputStream JavaDoc(((Part)part.get(0))._data);
192     }
193
194     /* ------------------------------------------------------------ */
195     public InputStream JavaDoc[] getInputStreams(String JavaDoc name)
196     {
197         List JavaDoc parts = (List JavaDoc)_partMap.getValues(name);
198         if (parts==null)
199             return null;
200         InputStream JavaDoc[] streams = new InputStream JavaDoc[parts.size()];
201         for (int i=0; i<streams.length; i++) {
202             streams[i] = new ByteArrayInputStream JavaDoc(((Part)parts.get(i))._data);
203         }
204         return streams;
205     }
206
207     /* ------------------------------------------------------------ */
208     /** Get the MIME parameters associated with a part.
209      * @param name The part name
210      * @return Hashtable of parameters
211      */

212     public Hashtable JavaDoc getParams(String JavaDoc name)
213     {
214         List JavaDoc part = (List JavaDoc)_partMap.getValues(name);
215         if (part==null)
216             return null;
217         return ((Part)part.get(0))._headers;
218     }
219
220     /* ------------------------------------------------------------ */
221     public Hashtable JavaDoc[] getMultipleParams(String JavaDoc name)
222     {
223         List JavaDoc parts = (List JavaDoc)_partMap.getValues(name);
224         if (parts==null)
225             return null;
226         Hashtable JavaDoc[] params = new Hashtable JavaDoc[parts.size()];
227         for (int i=0; i<params.length; i++) {
228             params[i] = ((Part)parts.get(i))._headers;
229         }
230         return params;
231     }
232
233     /* ------------------------------------------------------------ */
234     /** Get any file name associated with a part.
235      * @param name The part name
236      * @return The filename
237      */

238     public String JavaDoc getFilename(String JavaDoc name)
239     {
240         List JavaDoc part = (List JavaDoc)_partMap.getValues(name);
241         if (part==null)
242             return null;
243         return ((Part)part.get(0))._filename;
244     }
245
246     /* ------------------------------------------------------------ */
247     public String JavaDoc[] getFilenames(String JavaDoc name)
248     {
249         List JavaDoc parts = (List JavaDoc)_partMap.getValues(name);
250         if (parts==null)
251             return null;
252         String JavaDoc[] filenames = new String JavaDoc[parts.size()];
253         for (int i=0; i<filenames.length; i++) {
254             filenames[i] = ((Part)parts.get(i))._filename;
255         }
256         return filenames;
257     }
258
259     /* ------------------------------------------------------------ */
260     private void loadAllParts()
261         throws IOException JavaDoc
262     {
263         // Get first boundary
264
String JavaDoc line = _in.readLine();
265         if (!line.equals(_boundary))
266         {
267             log.warn(line);
268             throw new IOException JavaDoc("Missing initial multi part boundary");
269         }
270         
271         // Read each part
272
while (!_lastPart)
273         {
274             // Read Part headers
275
Part part = new Part();
276             
277             String JavaDoc content_disposition=null;
278             while ((line=_in.readLine())!=null)
279             {
280                 // If blank line, end of part headers
281
if (line.length()==0)
282                     break;
283
284                 if(log.isDebugEnabled())log.debug("LINE="+line);
285                 
286                 // place part header key and value in map
287
int c = line.indexOf(':',0);
288                 if (c>0)
289                 {
290                     String JavaDoc key = line.substring(0,c).trim().toLowerCase();
291                     String JavaDoc value = line.substring(c+1,line.length()).trim();
292                     String JavaDoc ev = (String JavaDoc) part._headers.get(key);
293                     part._headers.put(key,(ev!=null)?(ev+';'+value):value);
294                     if(log.isDebugEnabled())log.debug(key+": "+value);
295                     if (key.equals("content-disposition"))
296                         content_disposition=value;
297                 }
298             }
299
300             // Extract content-disposition
301
boolean form_data=false;
302             if (content_disposition==null)
303             {
304                 throw new IOException JavaDoc("Missing content-disposition");
305             }
306             
307             StringTokenizer JavaDoc tok =
308                 new StringTokenizer JavaDoc(content_disposition,";");
309             while (tok.hasMoreTokens())
310             {
311                 String JavaDoc t = tok.nextToken().trim();
312                 String JavaDoc tl = t.toLowerCase();
313                 if (t.startsWith("form-data"))
314                     form_data=true;
315                 else if (tl.startsWith("name="))
316                     part._name=value(t);
317                 else if (tl.startsWith("filename="))
318                     part._filename=value(t);
319             }
320
321             // Check disposition
322
if (!form_data)
323             {
324                 log.warn("Non form-data part in multipart/form-data");
325                 continue;
326             }
327             if (part._name==null || part._name.length()==0)
328             {
329                 log.warn("Part with no name in multipart/form-data");
330                 continue;
331             }
332             if(log.isDebugEnabled())log.debug("name="+part._name);
333             if(log.isDebugEnabled())log.debug("filename="+part._filename);
334             _partMap.add(part._name,part);
335             part._data=readBytes();
336         }
337     }
338
339     /* ------------------------------------------------------------ */
340     private byte[] readBytes()
341         throws IOException JavaDoc
342     {
343         ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
344
345         int c;
346         boolean cr=false;
347         boolean lf=false;
348         
349         // loop for all lines`
350
while (true)
351         {
352             int b=0;
353             while ((c=(_char!=-2)?_char:_in.read())!=-1)
354             {
355                 _char=-2;
356
357                 // look for CR and/or LF
358
if (c==13 || c==10)
359                 {
360                     if (c==13) _char=_in.read();
361                     break;
362                 }
363
364                 // look for boundary
365
if (b>=0 && b<_byteBoundary.length && c==_byteBoundary[b])
366                     b++;
367                 else
368                 {
369                     // this is not a boundary
370
if (cr) baos.write(13);
371                     if (lf) baos.write(10);
372                     cr=lf=false;
373                     
374                     if (b>0)
375                         baos.write(_byteBoundary,0,b);
376                     b=-1;
377                   
378                     baos.write(c);
379                 }
380             }
381
382             // check partial boundary
383
if ((b>0 && b<_byteBoundary.length-2) ||
384                 (b==_byteBoundary.length-1))
385             {
386                 if (cr) baos.write(13);
387                 if (lf) baos.write(10);
388                 cr=lf=false;
389                 baos.write(_byteBoundary,0,b);
390                 b=-1;
391             }
392             
393             // boundary match
394
if (b>0 || c==-1)
395             {
396                 if (b==_byteBoundary.length)
397                     _lastPart=true;
398                 if (_char==10) _char=-2;
399                 break;
400             }
401             
402             // handle CR LF
403
if (cr) baos.write(13);
404             if (lf) baos.write(10);
405             cr=(c==13);
406             lf=(c==10 || _char==10);
407             if (_char==10) _char=-2;
408         }
409         if(log.isTraceEnabled())log.trace(baos.toString());
410         return baos.toByteArray();
411     }
412     
413     
414     /* ------------------------------------------------------------ */
415     private String JavaDoc value(String JavaDoc nameEqualsValue)
416     {
417         String JavaDoc value =
418             nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
419         
420         int i=value.indexOf(';');
421         if (i>0)
422             value=value.substring(0,i);
423         if (value.startsWith("\""))
424         {
425             value=value.substring(1,value.indexOf('"',1));
426         }
427         
428         else
429         {
430             i=value.indexOf(' ');
431             if (i>0)
432                 value=value.substring(0,i);
433         }
434         return value;
435     }
436     
437     /* ------------------------------------------------------------ */
438     private class Part
439     {
440         String JavaDoc _name=null;
441         String JavaDoc _filename=null;
442         Hashtable JavaDoc _headers= new Hashtable JavaDoc(10);
443         byte[] _data=null;
444     }
445 };
446
Popular Tags