KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > misc > upload > MultipartStream


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64
65 package com.jcorporate.expresso.core.misc.upload;
66
67 /* ====================================================================
68  * The Apache Software License, Version 1.1
69  *
70  * Copyright (c) 2001 The Apache Software Foundation. All rights
71  * reserved.
72  *
73  * Redistribution and use in source and binary forms, with or without
74  * modification, are permitted provided that the following conditions
75  * are met:
76  *
77  * 1. Redistributions of source code must retain the above copyright
78  * notice, this list of conditions and the following disclaimer.
79  *
80  * 2. Redistributions in binary form must reproduce the above copyright
81  * notice, this list of conditions and the following disclaimer in
82  * the documentation and/or other materials provided with the
83  * distribution.
84  *
85  * 3. The end-user documentation included with the redistribution,
86  * if any, must include the following acknowledgment:
87  * "This product includes software developed by the
88  * Apache Software Foundation (http://www.apache.org/)."
89  * Alternately, this acknowledgment may appear in the software itself,
90  * if and wherever such third-party acknowledgments normally appear.
91  *
92  * 4. The names "Apache" and "Apache Software Foundation" and
93  * "Apache Turbine" must not be used to endorse or promote products
94  * derived from this software without prior written permission. For
95  * written permission, please contact apache@apache.org.
96  *
97  * 5. Products derived from this software may not be called "Apache",
98  * "Apache Turbine", nor may "Apache" appear in their name, without
99  * prior written permission of the Apache Software Foundation.
100  *
101  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
102  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
103  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
104  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
105  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
106  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
107  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
108  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
109  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
110  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
111  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
112  * SUCH DAMAGE.
113  * ====================================================================
114  *
115  * This software consists of voluntary contributions made by many
116  * individuals on behalf of the Apache Software Foundation. For more
117  * information on the Apache Software Foundation, please see
118  * <http://www.apache.org/>.
119  */

120 // Java stuff.
121

122 import java.io.IOException JavaDoc;
123 import java.io.InputStream JavaDoc;
124 import java.io.OutputStream JavaDoc;
125
126
127 /**
128  * This class can be used to process data streams conforming to MIME
129  * 'multipart' format as defined in <a
130  * HREF="http://www.ietf.org/rfc/rfc1521.txt">RFC&nbsp;1521</a>. Arbitrary
131  * large amouns of data in the stream can be processed under constant
132  * memory usage.
133  * <p/>
134  * <p>The format of the stream is defined in the following way:<br>
135  * <p/>
136  * <code>
137  * multipart-body := preamble 1*encapsulation close-delimiter epilogue<br>
138  * encapsulation := delimiter body CRLF<br>
139  * delimiter := "--" boundary CRLF<br>
140  * close-delimiter := "--" boudary "--"<br>
141  * preamble := &lt;ignore&gt;<br>
142  * epilogue := &lt;ignore&gt;<br>
143  * body := header-part CRLF body-part<br>
144  * header-part := 1*header CRLF<br>
145  * header := header-name ":" header-value<br>
146  * header-name := &lt;printable ascii characters except ":"&gt;<br>
147  * header-value := &lt;any ascii characters except CR & LF&gt;<br>
148  * body-data := &lt;arbitrary data&gt;<br>
149  * </code>
150  * <p/>
151  * <p>Note that body-data can contain another mulipart entity. There
152  * is limited support for single pass processing of such nested
153  * streams. The nested stream is <strong>required</strong> to have a
154  * boundary token of the same length as the parent stream (see {@link
155  * #setBoundary(byte[])}).
156  * <p/>
157  * <p>Here is an exaple of usage of this class.<br>
158  * <p/>
159  * <pre>
160  * try {
161  * MultipartStream multipartStream = new MultipartStream(input,
162  * boundary);
163  * boolean nextPart = malitPartStream.skipPreamble();
164  * OutputStream output;
165  * while(nextPart) {
166  * header = chunks.readHeader();
167  * // process headers
168  * // create some output stream
169  * multipartStream.readBodyPart(output);
170  * nextPart = multipartStream.readBoundary();
171  * }
172  * } catch(MultipartStream.MalformedStreamException e) {
173  * // the stream failed to follow required syntax
174  * } catch(IOException) {
175  * // a read or write error occurred
176  * }
177  * <p/>
178  * </pre>
179  *
180  * @author <a HREF="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
181  * @version $Id: MultipartStream.java,v 1.9 2004/11/18 02:03:27 lhamel Exp $
182  */

183 public class MultipartStream {
184
185     /**
186      * The maximum lenght of <code>header-part</code> that will be
187      * processed (10 kilobytes = 10240 bytes.
188      * )
189      */

190     public static final int HEADER_PART_SIZE_MAX = 10240;
191
192     /**
193      * The stream were data is read from.
194      */

195     protected InputStream JavaDoc input;
196
197     /**
198      * The lenght of boundary token plus leading <code>CRLF--</code>.
199      */

200     protected int boundaryLength;
201
202     /**
203      * The amount of data that must be kept in the buffer in order to
204      * detect delimiters reliably.
205      */

206     protected int keepRegion;
207
208     /**
209      * A byte sequence that partitions the stream.
210      */

211     protected byte[] boundary;
212
213     /**
214      * The lenght of the buffer used for processing.
215      */

216     protected int bufSize;
217
218     /**
219      * The default lenght of the buffer used for processing.
220      */

221     protected static final int DEFAULT_BUFSIZE = 4096;
222
223     /**
224      * The buffer used for processing.
225      */

226     protected byte[] buffer;
227
228     /**
229      * The index of first valid character in the buffer.
230      * <p/>
231      * 0 <= head < bufSize
232      */

233     protected int head;
234
235     /**
236      * The index of last valid characer in the buffer + 1.
237      * <p/>
238      * 0 <= tail <= bufSize
239      */

240     protected int tail;
241
242     /**
243      * A byte sequence that marks the end of <code>header-part</code>
244      * (<code>CRLFCRLF</code>).
245      */

246     protected static final byte[] HEADER_SEPARATOR = {0x0D, 0x0A, 0x0D, 0x0A};
247
248     /**
249      * A byte sequence that that follows a delimiter that will be
250      * followed by an encapsulation (<code>CRLF</code>).
251      */

252     protected static final byte[] FIELD_SEPARATOR = {0x0D, 0x0A};
253
254     /**
255      * A byte sequence that that follows a delimiter of the last
256      * encapsulation in the stream (<code>--</code>).
257      */

258     protected static final byte[] STREAM_TERMINATOR = {0x2D, 0x2D};
259
260     /**
261      * Constructs a MultipartStream with a custom size buffer.
262      * <p/>
263      * <p>Note that the buffer must be at least big enough to contain
264      * the boundary string, plus 4 characters for CR/LF and double
265      * dash, plus at least one byte of data. Too small buffer size
266      * setting will degrade performance.
267      *
268      * @param input The <code>InputStream</code> to serve as a data
269      * source.
270      * @param boundary The token used for dividing the stream into
271      * <code>encapsulations</code>.
272      * @param bufSize The size of the buffer to be used in bytes.
273      */

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

302     public MultipartStream(InputStream JavaDoc input, byte[] boundary)
303             throws IOException JavaDoc {
304         this(input, boundary, DEFAULT_BUFSIZE);
305     }
306
307     /**
308      * Reads a byte from the <code>buffer</code>, and refills it as
309      * neccessary.
310      *
311      * @return Next byte from the input stream.
312      * @throws IOException if there isn't any more data available.
313      */

314     public byte readByte()
315             throws IOException JavaDoc {
316
317         // Buffer depleted ?
318
if (head == tail) {
319             head = 0;
320
321             // Refill.
322
tail = input.read(buffer, head, bufSize);
323
324             if (tail == -1) {
325
326                 // No more data available.
327
throw new IOException JavaDoc("No more data is available");
328             }
329         }
330
331         return buffer[head++];
332     }
333
334     /**
335      * Skips a <code>boundary</code> token, and checks wether more
336      * <code>encapsulations</code> are contained in the stream.
337      *
338      * @return <code>True</code> if there are more encapsulations in
339      * this stream.
340      * @throws MalformedStreamException if the stream ends
341      * unexpecetedly or fails to follow required syntax.
342      */

343     public boolean readBoundary()
344             throws MalformedStreamException {
345         byte[] marker = new byte[2];
346         boolean nextChunk = false;
347         head += boundaryLength;
348
349         try {
350             marker[0] = readByte();
351             marker[1] = readByte();
352
353             if (arrayequals(marker, STREAM_TERMINATOR, 2)) {
354                 nextChunk = false;
355             } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) {
356                 nextChunk = true;
357             } else {
358                 throw new MalformedStreamException("Unexpected characters follow a boundary");
359             }
360         } catch (IOException JavaDoc e) {
361             throw new MalformedStreamException("Stream ended unexpectedly");
362         }
363
364         return nextChunk;
365     }
366
367     /**
368      * Changes the boundary token used for partitioning the stream.
369      * <p/>
370      * <p>This method allows single pass processing of nested
371      * multipart streams.
372      * <p/>
373      * <p>The boundary token of the nested stream is
374      * <code>required</code> to be of the same length as the boundary
375      * token in parent stream.
376      * <p/>
377      * <p>Restoring parent stream boundary token after processing of a
378      * nested stream is left ot the application. <br>
379      *
380      * @param boundary A boundary to be used for parsing of the nested
381      * stream.
382      * @throws IllegalBoundaryException if <code>boundary</code>
383      * has diffrent lenght than the one being currently in use.
384      */

385     public void setBoundary(byte[] boundary)
386             throws IllegalBoundaryException {
387         if (boundary.length != boundaryLength - 4) {
388             throw new IllegalBoundaryException("The length of a boundary token can not be changed");
389         }
390
391         System.arraycopy(boundary, 0, this.boundary, 4, boundary.length);
392     }
393
394     /**
395      * <p>Reads <code>header-part</code> of the current
396      * <code>encapsulation</code>
397      * <p/>
398      * <p>Headers are returned verbatim to the input stream, including
399      * traling <code>CRLF</code> marker. Parsing is left to the
400      * application.
401      * <p/>
402      * <p><strong>TODO</strong> allow limiting maximum header size to
403      * protect against abuse.<br>
404      *
405      * @return <code>header-part</code> of the current encapsulation.
406      * @throws MalformedStreamException if the stream ends
407      * unexpecetedly.
408      */

409     public String JavaDoc readHeaders()
410             throws MalformedStreamException {
411         int i = 0;
412         byte[] b = new byte[1];
413         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
414         int sizeMax = HEADER_PART_SIZE_MAX;
415         int size = 0;
416
417         while (i < 4) {
418             try {
419                 b[0] = readByte();
420             } catch (IOException JavaDoc e) {
421                 throw new MalformedStreamException("Stream ended unexpectedly");
422             }
423
424             size++;
425
426             if (b[0] == HEADER_SEPARATOR[i]) {
427                 i++;
428             } else {
429                 i = 0;
430             }
431             if (size <= sizeMax) {
432                 buf.append(new String JavaDoc(b));
433             }
434         }
435
436         return buf.toString();
437     }
438
439     /**
440      * Reads <code>body-data</code> from the current
441      * <code>encapsulation</code> and writes its contents into the
442      * output <code>Stream</code>.
443      * <p/>
444      * <p>Arbitrary large amouts of data can be processed by this
445      * method using a constant size buffer. (see {@link
446      * #MultipartStream(InputStream,byte[],int) constructor}).
447      *
448      * @param output The <code>Stream</code> to write data into.
449      * @return the amount of data written.
450      */

451     public int readBodyData(OutputStream JavaDoc output)
452             throws MalformedStreamException, IOException JavaDoc {
453         if (output == null) {
454             throw new IllegalArgumentException JavaDoc("OutputStream may not be null");
455         }
456
457         boolean done = false;
458         int pad;
459         int pos;
460         int bytesRead;
461         int total = 0;
462
463         while (!done) {
464
465             // Is boundary token present somewere in the buffer?
466
pos = findSeparator();
467
468             if (pos != -1) {
469
470                 // Write the rest of the data before the boundary.
471
output.write(buffer, head, pos - head);
472                 total += pos - head;
473                 head = pos;
474                 done = true;
475             } else {
476
477                 // Determine how much data should be kept in the
478
// buffer.
479
if (tail - head > keepRegion) {
480                     pad = keepRegion;
481                 } else {
482                     pad = tail - head;
483                 }
484
485                 // Write out the data belonging to the body-data.
486
output.write(buffer, head, tail - head - pad);
487
488                 // Move the data to the beging of the buffer.
489
total += tail - head - pad;
490                 System.arraycopy(buffer, tail - pad, buffer, 0, pad);
491
492                 // Refill buffer with new data.
493
head = 0;
494                 bytesRead = input.read(buffer, pad, bufSize - pad);
495
496                 // [pprrrrrrr]
497
if (bytesRead != -1) {
498                     tail = pad + bytesRead;
499                 } else {
500
501                     // The last pad amount is left in the buffer.
502
// Boundary can't be in there so write out the
503
// data you have and signal an error condition.
504
output.write(buffer, 0, pad);
505                     total += pad;
506                     throw new MalformedStreamException("Stream ended unexpectedly");
507                 }
508             }
509         }
510
511         return total;
512     }
513
514     /**
515      * Reads <code>body-data</code> from the current
516      * <code>encapsulation</code> and discards it.
517      * <p/>
518      * <p>Use this method to skip encapsulations you don't need or
519      * don't understand.
520      *
521      * @return The amount of data discarded.
522      */

523     public int discardBodyData()
524             throws MalformedStreamException, IOException JavaDoc {
525         boolean done = false;
526         int pad;
527         int pos;
528         int bytesRead;
529         int total = 0;
530
531         while (!done) {
532
533             // Is boundary token present somewere in the buffer?
534
pos = findSeparator();
535
536             if (pos != -1) {
537
538                 // Write the rest of the data before the boundary.
539
total += pos - head;
540                 head = pos;
541                 done = true;
542             } else {
543
544                 // Determine how much data should be kept in the
545
// buffer.
546
if (tail - head > keepRegion) {
547                     pad = keepRegion;
548                 } else {
549                     pad = tail - head;
550                 }
551
552                 total += tail - head - pad;
553
554                 // Move the data to the beging of the buffer.
555
System.arraycopy(buffer, tail - pad, buffer, 0, pad);
556
557                 // Refill buffer with new data.
558
head = 0;
559                 bytesRead = input.read(buffer, pad, bufSize - pad);
560
561                 // [pprrrrrrr]
562
if (bytesRead != -1) {
563                     tail = pad + bytesRead;
564                 } else {
565
566                     // The last pad amount is left in the buffer.
567
// Boundary can't be in there so signal an error
568
// condition.
569
total += pad;
570                     throw new MalformedStreamException("Stream ended unexpectedly");
571                 }
572             }
573         }
574
575         return total;
576     }
577
578     /**
579      * Finds the beginning of the first <code>encapsulation</code>.
580      *
581      * @return <code>True</code> if an <code>encapsulation</code> was
582      * found in the stream.
583      */

584     public boolean skipPreamble()
585             throws IOException JavaDoc {
586
587         // First delimiter may be not preceeded with a CRLF.
588
System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
589         boundaryLength = boundary.length - 2;
590
591         try {
592
593             // Discard all data up to the delimiter.
594
discardBodyData();
595
596             // Read boundary - if succeded, the stream contains an
597
// encapsulation.
598
return readBoundary();
599         } catch (MalformedStreamException e) {
600             return false;
601         } finally {
602
603             // Restore delimiter.
604
System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
605             boundaryLength = boundary.length;
606             boundary[0] = 0x0D;
607             boundary[1] = 0x0A;
608         }
609     }
610
611     /**
612      * Compares <code>count</code> first bytes in the arrays
613      * <code>a</code> and <code>b</code>.
614      *
615      * @param a The first array to compare.
616      * @param b The second array to compare.
617      * @param count How many bytes should be compared.
618      * @return <code>true</code> if <code>count</code> first bytes in
619      * arrays <code>a</code> and <code>b</code> are equal.
620      */

621     public static boolean arrayequals(byte[] a, byte[] b, int count) {
622         for (int i = 0; i < count; i++) {
623             if (a[i] != b[i]) {
624                 return false;
625             }
626         }
627
628         return true;
629     }
630
631     /**
632      * Searches a byte of specified value in the <code>buffer</code>
633      * starting at specified <code>position</code>.
634      *
635      * @param value the value to find.
636      * @param pos The starting position for searching.
637      * @return The position of byte found, counting from beginning of
638      * the <code>buffer</code>, or <code>-1</code> if not found.
639      */

640     protected int findByte(byte value, int pos) {
641         for (int i = pos; i < tail; i++) {
642             if (buffer[i] == value) {
643                 return i;
644             }
645         }
646
647         return -1;
648     }
649
650     /**
651      * Searches the <code>boundary</code> in <code>buffer</code>
652      * region delimited by <code>head</code> and <code>tail</code>.
653      *
654      * @return The position of the boundary found, counting from
655      * beginning of the <code>buffer</code>, or <code>-1</code> if not
656      * found.
657      */

658     protected int findSeparator() {
659         int first;
660         int match = 0;
661         int maxpos = tail - boundaryLength;
662
663         for (first = head;
664              (first <= maxpos) && (match != boundaryLength);
665              first++) {
666             first = findByte(boundary[0], first);
667
668             if (first == -1 || (first > maxpos)) {
669                 return -1;
670             }
671             for (match = 1; match < boundaryLength; match++) {
672                 if (buffer[first + match] != boundary[match]) {
673                     break;
674                 }
675             }
676         }
677         if (match == boundaryLength) {
678             return first - 1;
679         }
680
681         return -1;
682     }
683
684     /**
685      * Thrown to indicate that the input stream fails to follow the
686      * required syntax.
687      */

688     public class MalformedStreamException
689             extends IOException JavaDoc {
690         /**
691          * Constructs a <code>MalformedStreamException</code> with no
692          * detail message.
693          */

694         public MalformedStreamException() {
695             super();
696         }
697
698         /**
699          * Constructs an <code>MalformedStreamException</code> with
700          * the specified detail message.
701          *
702          * @param message The detail message.
703          */

704         public MalformedStreamException(String JavaDoc message) {
705             super(message);
706         }
707     }
708
709     /**
710      * Thrown upon attempt of setting an invalid boundary token.
711      */

712     public class IllegalBoundaryException
713             extends IOException JavaDoc {
714         /**
715          * Constructs an <code>IllegalBoundaryException</code> with no
716          * detail message.
717          */

718         public IllegalBoundaryException() {
719             super();
720         }
721
722         /**
723          * Constructs an <code>IllegalBoundaryException</code> with
724          * the specified detail message.
725          *
726          * @param message The detail message.
727          */

728         public IllegalBoundaryException(String JavaDoc message) {
729             super(message);
730         }
731     }
732
733
734     /*-------------------------------------------------------------
735
736 // These are the methods that were used to debug this stuff.
737
738 // Dump data.
739
740 protected void dump()
741
742 {
743
744         System.out.println("01234567890");
745
746         byte[] temp = new byte[buffer.length];
747
748         for(int i=0; i<buffer.length; i++)
749
750         {
751
752             if(buffer[i] == 0x0D || buffer[i] == 0x0A)
753
754             {
755
756                 temp[i] = 0x21;
757
758             }
759
760             else
761
762             {
763
764                 temp[i] = buffer[i];
765
766             }
767
768         }
769
770         System.out.println(new String(temp));
771
772         int i;
773
774         for(i=0; i<head; i++)
775
776             System.out.print(" ");
777
778         System.out.println("h");
779
780         for(i=0; i<tail; i++)
781
782             System.out.print(" ");
783
784         System.out.println("t");
785
786         System.out.flush();
787
788 }
789
790 // Main routine, for testing purposes only.
791
792 //
793
794 // @param args A String[] with the command line arguments.
795
796 // @exception Exception, a generic exception.
797
798 public static void main( String[] args )
799
800         throws Exception
801
802 {
803
804         File boundaryFile = new File("boundary.dat");
805
806         int boundarySize = (int)boundaryFile.length();
807
808         byte[] boundary = new byte[boundarySize];
809
810         FileInputStream input = new FileInputStream(boundaryFile);
811
812         input.read(boundary,0,boundarySize);
813
814         input = new FileInputStream("multipart.dat");
815
816         MultipartStream chunks = new MultipartStream(input, boundary);
817
818         int i = 0;
819
820         String header;
821
822         OutputStream output;
823
824         boolean nextChunk = chunks.skipPreamble();
825
826         while(nextChunk)
827
828         {
829
830             header = chunks.readHeaders();
831
832             System.out.println("!"+header+"!");
833
834             System.out.println("wrote part"+i+".dat");
835
836             output = new FileOutputStream("part"+(i++)+".dat");
837
838             chunks.readBodyData(output);
839
840             nextChunk = chunks.readBoundary();
841
842         }
843
844 }
845
846 */

847 }
Popular Tags