KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > oreilly > servlet > multipart > PartInputStream


1 // Copyright (C) 1999-2001 by Jason Hunter <jhunter_AT_acm_DOT_org>.
2
// All rights reserved. Use of this class is limited.
3
// Please see the LICENSE for more information.
4

5 package com.oreilly.servlet.multipart;
6
7 import java.io.FilterInputStream JavaDoc;
8 import java.io.IOException JavaDoc;
9 import javax.servlet.ServletInputStream JavaDoc;
10
11 /**
12  * A <code>PartInputStream</code> filters a <code>ServletInputStream</code>,
13  * providing access to a single MIME part contained with in which ends with
14  * the boundary specified. It uses buffering to provide maximum performance.
15  * <p>
16  * Note the <code>readLine</code> method of <code>ServletInputStream</code>
17  * has the annoying habit of adding a \r\n to the end of the last line. Since
18  * we want a byte-for-byte transfer, we have to cut those chars. This means
19  * that we must always maintain at least 2 characters in our buffer to allow
20  * us to trim when necessary.
21  *
22  * @author Geoff Soutter
23  * @author Jason Hunter
24  * @version 1.4, 2002/11/01, fix for "unexpected end of part" caused by
25  * boundary newlines split across buffers
26  * @version 1.3, 2001/05/21, fix to handle boundaries crossing 64K mark
27  * @version 1.2, 2001/02/07, added read(byte[]) implementation for safety
28  * @version 1.1, 2000/11/26, fixed available() to never return negative
29  * @version 1.0, 2000/10/27, initial revision
30  */

31 public class PartInputStream extends FilterInputStream JavaDoc {
32   /** boundary which "ends" the stream */
33   private String JavaDoc boundary;
34   
35   /** our buffer */
36   private byte [] buf = new byte[64*1024]; // 64k
37

38   /** number of bytes we've read into the buffer */
39   private int count;
40   
41   /** current position in the buffer */
42   private int pos;
43   
44   /** flag that indicates if we have encountered the boundary */
45   private boolean eof;
46     
47   /**
48    * Creates a <code>PartInputStream</code> which stops at the specified
49    * boundary from a <code>ServletInputStream<code>.
50    *
51    * @param in a servlet input stream.
52    * @param boundary the MIME boundary to stop at.
53    */

54   PartInputStream(ServletInputStream JavaDoc in,
55                   String JavaDoc boundary) throws IOException JavaDoc {
56     super(in);
57     this.boundary = boundary;
58   }
59
60   /**
61    * Fill up our buffer from the underlying input stream, and check for the
62    * boundary that signifies end-of-file. Users of this method must ensure
63    * that they leave exactly 2 characters in the buffer before calling this
64    * method (except the first time), so that we may only use these characters
65    * if a boundary is not found in the first line read.
66    *
67    * @exception IOException if an I/O error occurs.
68    */

69   private void fill() throws IOException JavaDoc
70   {
71     if (eof)
72       return;
73     
74     // as long as we are not just starting up
75
if (count > 0)
76     {
77       // if the caller left the requisite amount spare in the buffer
78
if (count - pos == 2) {
79         // copy it back to the start of the buffer
80
System.arraycopy(buf, pos, buf, 0, count - pos);
81         count -= pos;
82         pos = 0;
83       } else {
84         // should never happen, but just in case
85
throw new IllegalStateException JavaDoc("fill() detected illegal buffer state");
86       }
87     }
88     
89     // Try and fill the entire buffer, starting at count, line by line
90
// but never read so close to the end that we might split a boundary
91
// Thanks to Tony Chu, tony.chu@brio.com, for the -2 suggestion.
92
int read = 0;
93     int boundaryLength = boundary.length();
94     int maxRead = buf.length - boundaryLength - 2; // -2 is for /r/n
95
while (count < maxRead) {
96       // read a line
97
read = ((ServletInputStream JavaDoc)in).readLine(buf, count, buf.length - count);
98       // check for eof and boundary
99
if (read == -1) {
100         throw new IOException JavaDoc("unexpected end of part");
101       } else {
102         if (read >= boundaryLength) {
103           eof = true;
104           for (int i=0; i < boundaryLength; i++) {
105             if (boundary.charAt(i) != buf[count + i]) {
106               // Not the boundary!
107
eof = false;
108               break;
109             }
110           }
111           if (eof) {
112             break;
113           }
114         }
115       }
116       // success
117
count += read;
118     }
119   }
120   
121   /**
122    * See the general contract of the <code>read</code>
123    * method of <code>InputStream</code>.
124    * <p>
125    * Returns <code>-1</code> (end of file) when the MIME
126    * boundary of this part is encountered.
127    *
128    * @return the next byte of data, or <code>-1</code> if the end of the
129    * stream is reached.
130    * @exception IOException if an I/O error occurs.
131    */

132   public int read() throws IOException JavaDoc {
133     if (count - pos <= 2) {
134       fill();
135       if (count - pos <= 2) {
136         return -1;
137       }
138     }
139     return buf[pos++] & 0xff;
140   }
141
142   /**
143    * See the general contract of the <code>read</code>
144    * method of <code>InputStream</code>.
145    * <p>
146    * Returns <code>-1</code> (end of file) when the MIME
147    * boundary of this part is encountered.
148    *
149    * @param b the buffer into which the data is read.
150    * @return the total number of bytes read into the buffer, or
151    * <code>-1</code> if there is no more data because the end
152    * of the stream has been reached.
153    * @exception IOException if an I/O error occurs.
154    */

155   public int read(byte b[]) throws IOException JavaDoc {
156     return read(b, 0, b.length);
157   }
158
159   /**
160    * See the general contract of the <code>read</code>
161    * method of <code>InputStream</code>.
162    * <p>
163    * Returns <code>-1</code> (end of file) when the MIME
164    * boundary of this part is encountered.
165    *
166    * @param b the buffer into which the data is read.
167    * @param off the start offset of the data.
168    * @param len the maximum number of bytes read.
169    * @return the total number of bytes read into the buffer, or
170    * <code>-1</code> if there is no more data because the end
171    * of the stream has been reached.
172    * @exception IOException if an I/O error occurs.
173    */

174   public int read(byte b[], int off, int len) throws IOException JavaDoc
175   {
176     int total = 0;
177     if (len == 0) {
178       return 0;
179     }
180
181     int avail = count - pos - 2;
182     if (avail <= 0) {
183       fill();
184       avail = count - pos - 2;
185       if(avail <= 0) {
186         return -1;
187       }
188     }
189     int copy = Math.min(len, avail);
190     System.arraycopy(buf, pos, b, off, copy);
191     pos += copy;
192     total += copy;
193       
194     while (total < len) {
195       fill();
196       avail = count - pos - 2;
197       if(avail <= 0) {
198         return total;
199       }
200       copy = Math.min(len - total, avail);
201       System.arraycopy(buf, pos, b, off + total, copy);
202       pos += copy;
203       total += copy;
204     }
205     return total;
206   }
207
208   /**
209    * Returns the number of bytes that can be read from this input stream
210    * without blocking. This is a standard <code>InputStream</code> idiom
211    * to deal with buffering gracefully, and is not same as the length of the
212    * part arriving in this stream.
213    *
214    * @return the number of bytes that can be read from the input stream
215    * without blocking.
216    * @exception IOException if an I/O error occurs.
217    */

218   public int available() throws IOException JavaDoc {
219     int avail = (count - pos - 2) + in.available();
220     // Never return a negative value
221
return (avail < 0 ? 0 : avail);
222   }
223
224   /**
225    * Closes this input stream and releases any system resources
226    * associated with the stream.
227    * <p>
228    * This method will read any unread data in the MIME part so that the next
229    * part starts an an expected place in the parent <code>InputStream</code>.
230    * Note that if the client code forgets to call this method on error,
231    * <code>MultipartParser</code> will call it automatically if you call
232    * <code>readNextPart()</code>.
233    *
234    * @exception IOException if an I/O error occurs.
235    */

236   public void close() throws IOException JavaDoc {
237     if (!eof) {
238       while (read(buf, 0, buf.length) != -1)
239         ; // do nothing
240
}
241   }
242 }
243
Popular Tags