KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > fileupload > MultipartStream


1 /*
2  * $Header: /home/cvs/jakarta-commons/fileupload/src/java/org/apache/commons/fileupload/MultipartStream.java,v 1.12 2003/06/01 00:18:13 martinc Exp $
3  * $Revision: 1.12 $
4  * $Date: 2003/06/01 00:18:13 $
5  *
6  * ====================================================================
7  *
8  * The Apache Software License, Version 1.1
9  *
10  * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
11  * reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  *
17  * 1. Redistributions of source code must retain the above copyright
18  * notice, this list of conditions and the following disclaimer.
19  *
20  * 2. Redistributions in binary form must reproduce the above copyright
21  * notice, this list of conditions and the following disclaimer in
22  * the documentation and/or other materials provided with the
23  * distribution.
24  *
25  * 3. The end-user documentation included with the redistribution, if
26  * any, must include the following acknowlegement:
27  * "This product includes software developed by the
28  * Apache Software Foundation (http://www.apache.org/)."
29  * Alternately, this acknowlegement may appear in the software itself,
30  * if and wherever such third-party acknowlegements normally appear.
31  *
32  * 4. The names "The Jakarta Project", "Commons", and "Apache Software
33  * Foundation" must not be used to endorse or promote products derived
34  * from this software without prior written permission. For written
35  * permission, please contact apache@apache.org.
36  *
37  * 5. Products derived from this software may not be called "Apache"
38  * nor may "Apache" appear in their names without prior written
39  * permission of the Apache Group.
40  *
41  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52  * SUCH DAMAGE.
53  * ====================================================================
54  *
55  * This software consists of voluntary contributions made by many
56  * individuals on behalf of the Apache Software Foundation. For more
57  * information on the Apache Software Foundation, please see
58  * <http://www.apache.org/>.
59  *
60  */

61
62
63 package org.apache.commons.fileupload;
64
65
66 import java.io.ByteArrayOutputStream JavaDoc;
67 import java.io.IOException JavaDoc;
68 import java.io.InputStream JavaDoc;
69 import java.io.OutputStream JavaDoc;
70 import java.io.UnsupportedEncodingException JavaDoc;
71
72
73 /**
74  * <p> Low level API for processing file uploads.
75  *
76  * <p> This class can be used to process data streams conforming to MIME
77  * 'multipart' format as defined in
78  * <a HREF="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily
79  * large amounts of data in the stream can be processed under constant
80  * memory usage.
81  *
82  * <p> The format of the stream is defined in the following way:<br>
83  *
84  * <code>
85  * multipart-body := preamble 1*encapsulation close-delimiter epilogue<br>
86  * encapsulation := delimiter body CRLF<br>
87  * delimiter := "--" boundary CRLF<br>
88  * close-delimiter := "--" boudary "--"<br>
89  * preamble := &lt;ignore&gt;<br>
90  * epilogue := &lt;ignore&gt;<br>
91  * body := header-part CRLF body-part<br>
92  * header-part := 1*header CRLF<br>
93  * header := header-name ":" header-value<br>
94  * header-name := &lt;printable ascii characters except ":"&gt;<br>
95  * header-value := &lt;any ascii characters except CR & LF&gt;<br>
96  * body-data := &lt;arbitrary data&gt;<br>
97  * </code>
98  *
99  * <p>Note that body-data can contain another mulipart entity. There
100  * is limited support for single pass processing of such nested
101  * streams. The nested stream is <strong>required</strong> to have a
102  * boundary token of the same length as the parent stream (see {@link
103  * #setBoundary(byte[])}).
104  *
105  * <p>Here is an exaple of usage of this class.<br>
106  *
107  * <pre>
108  * try {
109  * MultipartStream multipartStream = new MultipartStream(input,
110  * boundary);
111  * boolean nextPart = malitPartStream.skipPreamble();
112  * OutputStream output;
113  * while(nextPart) {
114  * header = chunks.readHeader();
115  * // process headers
116  * // create some output stream
117  * multipartStream.readBodyPart(output);
118  * nextPart = multipartStream.readBoundary();
119  * }
120  * } catch(MultipartStream.MalformedStreamException e) {
121  * // the stream failed to follow required syntax
122  * } catch(IOException) {
123  * // a read or write error occurred
124  * }
125  *
126  * </pre>
127  *
128  * @author <a HREF="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
129  * @author <a HREF="mailto:martinc@apache.org">Martin Cooper</a>
130  * @author Sean C. Sullivan
131  *
132  * @version $Id: MultipartStream.java,v 1.12 2003/06/01 00:18:13 martinc Exp $
133  */

134 public class MultipartStream
135 {
136
137     // ----------------------------------------------------- Manifest constants
138

139
140     /**
141      * The maximum length of <code>header-part</code> that will be
142      * processed (10 kilobytes = 10240 bytes.).
143      */

144     public static final int HEADER_PART_SIZE_MAX = 10240;
145
146
147     /**
148      * The default length of the buffer used for processing a request.
149      */

150     protected static final int DEFAULT_BUFSIZE = 4096;
151
152
153     /**
154      * A byte sequence that marks the end of <code>header-part</code>
155      * (<code>CRLFCRLF</code>).
156      */

157     protected static final byte[] HEADER_SEPARATOR = {0x0D, 0x0A, 0x0D, 0x0A};
158
159
160     /**
161      * A byte sequence that that follows a delimiter that will be
162      * followed by an encapsulation (<code>CRLF</code>).
163      */

164     protected static final byte[] FIELD_SEPARATOR = { 0x0D, 0x0A };
165
166
167     /**
168      * A byte sequence that that follows a delimiter of the last
169      * encapsulation in the stream (<code>--</code>).
170      */

171     protected static final byte[] STREAM_TERMINATOR = { 0x2D, 0x2D };
172
173
174     // ----------------------------------------------------------- Data members
175

176
177     /**
178      * The input stream from which data is read.
179      */

180     private InputStream JavaDoc input;
181
182
183     /**
184      * The length of the boundary token plus the leading <code>CRLF--</code>.
185      */

186     private int boundaryLength;
187
188
189     /**
190      * The amount of data, in bytes, that must be kept in the buffer in order
191      * to detect delimiters reliably.
192      */

193     private int keepRegion;
194
195
196     /**
197      * The byte sequence that partitions the stream.
198      */

199     private byte[] boundary;
200
201
202     /**
203      * The length of the buffer used for processing the request.
204      */

205     private int bufSize;
206
207
208     /**
209      * The buffer used for processing the request.
210      */

211     private byte[] buffer;
212
213
214     /**
215      * The index of first valid character in the buffer.
216      * <br>
217      * 0 <= head < bufSize
218      */

219     private int head;
220
221
222     /**
223      * The index of last valid characer in the buffer + 1.
224      * <br>
225      * 0 <= tail <= bufSize
226      */

227     private int tail;
228
229
230     /**
231      * The content encoding to use when reading headers.
232      */

233     private String JavaDoc headerEncoding;
234
235
236     // ----------------------------------------------------------- Constructors
237

238
239     /**
240      * Default constructor.
241      *
242      * @see #MultipartStream(InputStream, byte[], int)
243      * @see #MultipartStream(InputStream, byte[])
244      *
245      */

246     public MultipartStream()
247     {
248     }
249
250
251     /**
252      * <p> Constructs a <code>MultipartStream</code> with a custom size buffer.
253      *
254      * <p> Note that the buffer must be at least big enough to contain the
255      * boundary string, plus 4 characters for CR/LF and double dash, plus at
256      * least one byte of data. Too small a buffer size setting will degrade
257      * performance.
258      *
259      * @param input The <code>InputStream</code> to serve as a data source.
260      * @param boundary The token used for dividing the stream into
261      * <code>encapsulations</code>.
262      * @param bufSize The size of the buffer to be used, in bytes.
263      *
264      *
265      * @see #MultipartStream()
266      * @see #MultipartStream(InputStream, byte[])
267      *
268      */

269     public MultipartStream(InputStream JavaDoc input,
270                            byte[] boundary,
271                            int bufSize)
272     {
273         this.input = input;
274         this.bufSize = bufSize;
275         this.buffer = new byte[bufSize];
276
277         // We prepend CR/LF to the boundary to chop trailng CR/LF from
278
// body-data tokens.
279
this.boundary = new byte[boundary.length + 4];
280         this.boundaryLength = boundary.length + 4;
281         this.keepRegion = boundary.length + 3;
282         this.boundary[0] = 0x0D;
283         this.boundary[1] = 0x0A;
284         this.boundary[2] = 0x2D;
285         this.boundary[3] = 0x2D;
286         System.arraycopy(boundary, 0, this.boundary, 4, boundary.length);
287
288         head = 0;
289         tail = 0;
290     }
291
292
293     /**
294      * <p> Constructs a <code>MultipartStream</code> with a default size buffer.
295      *
296      * @param input The <code>InputStream</code> to serve as a data source.
297      * @param boundary The token used for dividing the stream into
298      * <code>encapsulations</code>.
299      *
300      * @exception IOException when an error occurs.
301      *
302      * @see #MultipartStream()
303      * @see #MultipartStream(InputStream, byte[], int)
304      *
305      */

306     public MultipartStream(InputStream JavaDoc input,
307                            byte[] boundary)
308         throws IOException JavaDoc
309     {
310         this(input, boundary, DEFAULT_BUFSIZE);
311     }
312
313
314     // --------------------------------------------------------- Public methods
315

316
317     /**
318      * Retrieves the character encoding used when reading the headers of an
319      * individual part. When not specified, or <code>null</code>, the platform
320      * default encoding is used.
321
322      *
323      * @return The encoding used to read part headers.
324      */

325     public String JavaDoc getHeaderEncoding()
326     {
327         return headerEncoding;
328     }
329
330
331     /**
332      * Specifies the character encoding to be used when reading the headers of
333      * individual parts. When not specified, or <code>null</code>, the platform
334      * default encoding is used.
335      *
336      * @param encoding The encoding used to read part headers.
337      */

338     public void setHeaderEncoding(String JavaDoc encoding)
339     {
340         headerEncoding = encoding;
341     }
342
343
344     /**
345      * Reads a byte from the <code>buffer</code>, and refills it as
346      * necessary.
347      *
348      * @return The next byte from the input stream.
349      *
350      * @exception IOException if there is no more data available.
351      */

352     public byte readByte()
353         throws IOException JavaDoc
354     {
355         // Buffer depleted ?
356
if (head == tail)
357         {
358             head = 0;
359             // Refill.
360
tail = input.read(buffer, head, bufSize);
361             if (tail == -1)
362             {
363                 // No more data available.
364
throw new IOException JavaDoc("No more data is available");
365             }
366         }
367         return buffer[head++];
368     }
369
370
371     /**
372      * Skips a <code>boundary</code> token, and checks whether more
373      * <code>encapsulations</code> are contained in the stream.
374      *
375      * @return <code>true</code> if there are more encapsulations in
376      * this stream; <code>false</code> otherwise.
377      *
378      * @exception MalformedStreamException if the stream ends unexpecetedly or
379      * fails to follow required syntax.
380      */

381     public boolean readBoundary()
382         throws MalformedStreamException
383     {
384         byte[] marker = new byte[2];
385         boolean nextChunk = false;
386
387         head += boundaryLength;
388         try
389         {
390             marker[0] = readByte();
391             marker[1] = readByte();
392             if (arrayequals(marker, STREAM_TERMINATOR, 2))
393             {
394                 nextChunk = false;
395             }
396             else if (arrayequals(marker, FIELD_SEPARATOR, 2))
397             {
398                 nextChunk = true;
399             }
400             else
401             {
402                 throw new MalformedStreamException(
403                         "Unexpected characters follow a boundary");
404             }
405         }
406         catch (IOException JavaDoc e)
407         {
408             throw new MalformedStreamException("Stream ended unexpectedly");
409         }
410         return nextChunk;
411     }
412
413
414     /**
415      * <p>Changes the boundary token used for partitioning the stream.
416      *
417      * <p>This method allows single pass processing of nested multipart
418      * streams.
419      *
420      * <p>The boundary token of the nested stream is <code>required</code>
421      * to be of the same length as the boundary token in parent stream.
422      *
423      * <p>Restoring the parent stream boundary token after processing of a
424      * nested stream is left to the application.
425      *
426      * @param boundary The boundary to be used for parsing of the nested
427      * stream.
428      *
429      * @exception IllegalBoundaryException if the <code>boundary</code>
430      * has a different length than the one
431      * being currently parsed.
432      */

433     public void setBoundary(byte[] boundary)
434         throws IllegalBoundaryException
435     {
436         if (boundary.length != boundaryLength - 4)
437         {
438             throw new IllegalBoundaryException(
439                     "The length of a boundary token can not be changed");
440         }
441         System.arraycopy(boundary, 0, this.boundary, 4, boundary.length);
442     }
443
444
445     /**
446      * <p>Reads the <code>header-part</code> of the current
447      * <code>encapsulation</code>.
448      *
449      * <p>Headers are returned verbatim to the input stream, including the
450      * trailing <code>CRLF</code> marker. Parsing is left to the
451      * application.
452      *
453      * <p><strong>TODO</strong> allow limiting maximum header size to
454      * protect against abuse.
455      *
456      * @return The <code>header-part</code> of the current encapsulation.
457      *
458      * @exception MalformedStreamException if the stream ends unexpecetedly.
459      */

460     public String JavaDoc readHeaders()
461         throws MalformedStreamException
462     {
463         int i = 0;
464         byte b[] = new byte[1];
465         // to support multi-byte characters
466
ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
467         int sizeMax = HEADER_PART_SIZE_MAX;
468         int size = 0;
469         while (i < 4)
470         {
471             try
472             {
473                 b[0] = readByte();
474             }
475             catch (IOException JavaDoc e)
476             {
477                 throw new MalformedStreamException("Stream ended unexpectedly");
478             }
479             size++;
480             if (b[0] == HEADER_SEPARATOR[i])
481             {
482                 i++;
483             }
484             else
485             {
486                 i = 0;
487             }
488             if (size <= sizeMax)
489             {
490                 baos.write(b[0]);
491             }
492         }
493
494         String JavaDoc headers = null;
495         if (headerEncoding != null)
496         {
497             try
498             {
499                 headers = baos.toString(headerEncoding);
500             }
501             catch (UnsupportedEncodingException JavaDoc e)
502             {
503                 // Fall back to platform default if specified encoding is not
504
// supported.
505
headers = baos.toString();
506             }
507         }
508         else
509         {
510             headers = baos.toString();
511         }
512
513         return headers;
514     }
515
516
517     /**
518      * <p>Reads <code>body-data</code> from the current
519      * <code>encapsulation</code> and writes its contents into the
520      * output <code>Stream</code>.
521      *
522      * <p>Arbitrary large amounts of data can be processed by this
523      * method using a constant size buffer. (see {@link
524      * #MultipartStream(InputStream,byte[],int) constructor}).
525      *
526      * @param output The <code>Stream</code> to write data into.
527      *
528      * @return the amount of data written.
529      *
530      * @exception MalformedStreamException if the stream ends unexpectedly.
531      * @exception IOException if an i/o error occurs.
532      */

533     public int readBodyData(OutputStream JavaDoc output)
534         throws MalformedStreamException,
535                IOException JavaDoc
536     {
537         boolean done = false;
538         int pad;
539         int pos;
540         int bytesRead;
541         int total = 0;
542         while (!done)
543         {
544             // Is boundary token present somewere in the buffer?
545
pos = findSeparator();
546             if (pos != -1)
547             {
548                 // Write the rest of the data before the boundary.
549
output.write(buffer, head, pos - head);
550                 total += pos - head;
551                 head = pos;
552                 done = true;
553             }
554             else
555             {
556                 // Determine how much data should be kept in the
557
// buffer.
558
if (tail - head > keepRegion)
559                 {
560                     pad = keepRegion;
561                 }
562                 else
563                 {
564                     pad = tail - head;
565                 }
566                 // Write out the data belonging to the body-data.
567
output.write(buffer, head, tail - head - pad);
568
569                 // Move the data to the beging of the buffer.
570
total += tail - head - pad;
571                 System.arraycopy(buffer, tail - pad, buffer, 0, pad);
572
573                 // Refill buffer with new data.
574
head = 0;
575                 bytesRead = input.read(buffer, pad, bufSize - pad);
576
577                 // [pprrrrrrr]
578
if (bytesRead != -1)
579                 {
580                     tail = pad + bytesRead;
581                 }
582                 else
583                 {
584                     // The last pad amount is left in the buffer.
585
// Boundary can't be in there so write out the
586
// data you have and signal an error condition.
587
output.write(buffer, 0, pad);
588                     output.flush();
589                     total += pad;
590                     throw new MalformedStreamException(
591                             "Stream ended unexpectedly");
592                 }
593             }
594         }
595         output.flush();
596         return total;
597     }
598
599
600     /**
601      * <p> Reads <code>body-data</code> from the current
602      * <code>encapsulation</code> and discards it.
603      *
604      * <p>Use this method to skip encapsulations you don't need or don't
605      * understand.
606      *
607      * @return The amount of data discarded.
608      *
609      * @exception MalformedStreamException if the stream ends unexpectedly.
610      * @exception IOException if an i/o error occurs.
611      */

612     public int discardBodyData()
613         throws MalformedStreamException,
614                IOException JavaDoc
615     {
616         boolean done = false;
617         int pad;
618         int pos;
619         int bytesRead;
620         int total = 0;
621         while (!done)
622         {
623             // Is boundary token present somewere in the buffer?
624
pos = findSeparator();
625             if (pos != -1)
626             {
627                 // Write the rest of the data before the boundary.
628
total += pos - head;
629                 head = pos;
630                 done = true;
631             }
632             else
633             {
634                 // Determine how much data should be kept in the
635
// buffer.
636
if (tail - head > keepRegion)
637                 {
638                     pad = keepRegion;
639                 }
640                 else
641                 {
642                     pad = tail - head;
643                 }
644                 total += tail - head - pad;
645
646                 // Move the data to the beging of the buffer.
647
System.arraycopy(buffer, tail - pad, buffer, 0, pad);
648
649                 // Refill buffer with new data.
650
head = 0;
651                 bytesRead = input.read(buffer, pad, bufSize - pad);
652
653                 // [pprrrrrrr]
654
if (bytesRead != -1)
655                 {
656                     tail = pad + bytesRead;
657                 }
658                 else
659                 {
660                     // The last pad amount is left in the buffer.
661
// Boundary can't be in there so signal an error
662
// condition.
663
total += pad;
664                     throw new MalformedStreamException(
665                             "Stream ended unexpectedly");
666                 }
667             }
668         }
669         return total;
670     }
671
672
673     /**
674      * Finds the beginning of the first <code>encapsulation</code>.
675      *
676      * @return <code>true</code> if an <code>encapsulation</code> was found in
677      * the stream.
678      *
679      * @exception IOException if an i/o error occurs.
680      */

681     public boolean skipPreamble()
682         throws IOException JavaDoc
683     {
684         // First delimiter may be not preceeded with a CRLF.
685
System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
686         boundaryLength = boundary.length - 2;
687         try
688         {
689             // Discard all data up to the delimiter.
690
discardBodyData();
691
692             // Read boundary - if succeded, the stream contains an
693
// encapsulation.
694
return readBoundary();
695         }
696         catch (MalformedStreamException e)
697         {
698             return false;
699         }
700         finally
701         {
702             // Restore delimiter.
703
System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
704             boundaryLength = boundary.length;
705             boundary[0] = 0x0D;
706             boundary[1] = 0x0A;
707         }
708     }
709
710
711     /**
712      * Compares <code>count</code> first bytes in the arrays
713      * <code>a</code> and <code>b</code>.
714      *
715      * @param a The first array to compare.
716      * @param b The second array to compare.
717      * @param count How many bytes should be compared.
718      *
719      * @return <code>true</code> if <code>count</code> first bytes in arrays
720      * <code>a</code> and <code>b</code> are equal.
721      */

722     public static boolean arrayequals(byte[] a,
723                                       byte[] b,
724                                       int count)
725     {
726         for (int i = 0; i < count; i++)
727         {
728             if (a[i] != b[i])
729             {
730                 return false;
731             }
732         }
733         return true;
734     }
735
736
737     /**
738      * Searches for a byte of specified value in the <code>buffer</code>,
739      * starting at the specified <code>position</code>.
740      *
741      * @param value The value to find.
742      * @param pos The starting position for searching.
743      *
744      * @return The position of byte found, counting from beginning of the
745      * <code>buffer</code>, or <code>-1</code> if not found.
746      */

747     protected int findByte(byte value,
748                            int pos)
749     {
750         for (int i = pos; i < tail; i++)
751         {
752             if (buffer[i] == value)
753             {
754                 return i;
755             }
756         }
757
758         return -1;
759     }
760
761
762     /**
763      * Searches for the <code>boundary</code> in the <code>buffer</code>
764      * region delimited by <code>head</code> and <code>tail</code>.
765      *
766      * @return The position of the boundary found, counting from the
767      * beginning of the <code>buffer</code>, or <code>-1</code> if
768      * not found.
769      */

770     protected int findSeparator()
771     {
772         int first;
773         int match = 0;
774         int maxpos = tail - boundaryLength;
775         for (first = head;
776              (first <= maxpos) && (match != boundaryLength);
777              first++)
778         {
779             first = findByte(boundary[0], first);
780             if (first == -1 || (first > maxpos))
781             {
782                 return -1;
783             }
784             for (match = 1; match < boundaryLength; match++)
785             {
786                 if (buffer[first + match] != boundary[match])
787                 {
788                     break;
789                 }
790             }
791         }
792         if (match == boundaryLength)
793         {
794             return first - 1;
795         }
796         return -1;
797     }
798
799     /**
800      * Returns a string representation of this object.
801      *
802      * @return The string representation of this object.
803      */

804     public String JavaDoc toString()
805     {
806         StringBuffer JavaDoc sbTemp = new StringBuffer JavaDoc();
807         sbTemp.append("boundary='");
808         sbTemp.append(String.valueOf(boundary));
809         sbTemp.append("'\nbufSize=");
810         sbTemp.append(bufSize);
811         return sbTemp.toString();
812     }
813
814     /**
815      * Thrown to indicate that the input stream fails to follow the
816      * required syntax.
817      */

818     public class MalformedStreamException
819         extends IOException JavaDoc
820     {
821         /**
822          * Constructs a <code>MalformedStreamException</code> with no
823          * detail message.
824          */

825         public MalformedStreamException()
826         {
827             super();
828         }
829
830         /**
831          * Constructs an <code>MalformedStreamException</code> with
832          * the specified detail message.
833          *
834          * @param message The detail message.
835          */

836         public MalformedStreamException(String JavaDoc message)
837         {
838             super(message);
839         }
840     }
841
842
843     /**
844      * Thrown upon attempt of setting an invalid boundary token.
845      */

846     public class IllegalBoundaryException
847         extends IOException JavaDoc
848     {
849         /**
850          * Constructs an <code>IllegalBoundaryException</code> with no
851          * detail message.
852          */

853         public IllegalBoundaryException()
854         {
855             super();
856         }
857
858         /**
859          * Constructs an <code>IllegalBoundaryException</code> with
860          * the specified detail message.
861          *
862          * @param message The detail message.
863          */

864         public IllegalBoundaryException(String JavaDoc message)
865         {
866             super(message);
867         }
868     }
869
870
871     // ------------------------------------------------------ Debugging methods
872

873
874     // These are the methods that were used to debug this stuff.
875
/*
876
877     // Dump data.
878     protected void dump()
879     {
880         System.out.println("01234567890");
881         byte[] temp = new byte[buffer.length];
882         for(int i=0; i<buffer.length; i++)
883         {
884             if (buffer[i] == 0x0D || buffer[i] == 0x0A)
885             {
886                 temp[i] = 0x21;
887             }
888             else
889             {
890                 temp[i] = buffer[i];
891             }
892         }
893         System.out.println(new String(temp));
894         int i;
895         for (i=0; i<head; i++)
896             System.out.print(" ");
897         System.out.println("h");
898         for (i=0; i<tail; i++)
899             System.out.print(" ");
900         System.out.println("t");
901         System.out.flush();
902     }
903
904     // Main routine, for testing purposes only.
905     //
906     // @param args A String[] with the command line arguments.
907     // @exception Exception, a generic exception.
908     public static void main( String[] args )
909         throws Exception
910     {
911         File boundaryFile = new File("boundary.dat");
912         int boundarySize = (int)boundaryFile.length();
913         byte[] boundary = new byte[boundarySize];
914         FileInputStream input = new FileInputStream(boundaryFile);
915         input.read(boundary,0,boundarySize);
916
917         input = new FileInputStream("multipart.dat");
918         MultipartStream chunks = new MultipartStream(input, boundary);
919
920         int i = 0;
921         String header;
922         OutputStream output;
923         boolean nextChunk = chunks.skipPreamble();
924         while (nextChunk)
925         {
926             header = chunks.readHeaders();
927             System.out.println("!"+header+"!");
928             System.out.println("wrote part"+i+".dat");
929             output = new FileOutputStream("part"+(i++)+".dat");
930             chunks.readBodyData(output);
931             nextChunk = chunks.readBoundary();
932         }
933     }
934
935     */

936 }
937
Popular Tags