001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.fileupload; 018 019 import java.io.ByteArrayOutputStream; 020 import java.io.IOException; 021 import java.io.InputStream; 022 import java.io.OutputStream; 023 import java.io.UnsupportedEncodingException; 024 025 import org.apache.commons.fileupload.util.Closeable; 026 import org.apache.commons.fileupload.util.Streams; 027 028 /** 029 * <p> Low level API for processing file uploads. 030 * 031 * <p> This class can be used to process data streams conforming to MIME 032 * 'multipart' format as defined in 033 * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily 034 * large amounts of data in the stream can be processed under constant 035 * memory usage. 036 * 037 * <p> The format of the stream is defined in the following way:<br> 038 * 039 * <code> 040 * multipart-body := preamble 1*encapsulation close-delimiter epilogue<br> 041 * encapsulation := delimiter body CRLF<br> 042 * delimiter := "--" boundary CRLF<br> 043 * close-delimiter := "--" boudary "--"<br> 044 * preamble := <ignore><br> 045 * epilogue := <ignore><br> 046 * body := header-part CRLF body-part<br> 047 * header-part := 1*header CRLF<br> 048 * header := header-name ":" header-value<br> 049 * header-name := <printable ascii characters except ":"><br> 050 * header-value := <any ascii characters except CR & LF><br> 051 * body-data := <arbitrary data><br> 052 * </code> 053 * 054 * <p>Note that body-data can contain another mulipart entity. There 055 * is limited support for single pass processing of such nested 056 * streams. The nested stream is <strong>required</strong> to have a 057 * boundary token of the same length as the parent stream (see {@link 058 * #setBoundary(byte[])}). 059 * 060 * <p>Here is an example of usage of this class.<br> 061 * 062 * <pre> 063 * try { 064 * MultipartStream multipartStream = new MultipartStream(input, 065 * boundary); 066 * boolean nextPart = multipartStream.skipPreamble(); 067 * OutputStream output; 068 * while(nextPart) { 069 * header = chunks.readHeader(); 070 * // process headers 071 * // create some output stream 072 * multipartStream.readBodyPart(output); 073 * nextPart = multipartStream.readBoundary(); 074 * } 075 * } catch(MultipartStream.MalformedStreamException e) { 076 * // the stream failed to follow required syntax 077 * } catch(IOException) { 078 * // a read or write error occurred 079 * } 080 * 081 * </pre> 082 * 083 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a> 084 * @author <a href="mailto:martinc@apache.org">Martin Cooper</a> 085 * @author Sean C. Sullivan 086 * 087 * @version $Id: MultipartStream.java 607869 2008-01-01 16:42:17Z jochen $ 088 */ 089 public class MultipartStream { 090 /** 091 * Internal class, which is used to invoke the 092 * {@link ProgressListener}. 093 */ 094 static class ProgressNotifier { 095 /** The listener to invoke. 096 */ 097 private final ProgressListener listener; 098 /** Number of expected bytes, if known, or -1. 099 */ 100 private final long contentLength; 101 /** Number of bytes, which have been read so far. 102 */ 103 private long bytesRead; 104 /** Number of items, which have been read so far. 105 */ 106 private int items; 107 /** Creates a new instance with the given listener 108 * and content length. 109 * @param pListener The listener to invoke. 110 * @param pContentLength The expected content length. 111 */ 112 ProgressNotifier(ProgressListener pListener, long pContentLength) { 113 listener = pListener; 114 contentLength = pContentLength; 115 } 116 /** Called to indicate that bytes have been read. 117 * @param pBytes Number of bytes, which have been read. 118 */ 119 void noteBytesRead(int pBytes) { 120 /* Indicates, that the given number of bytes have been read from 121 * the input stream. 122 */ 123 bytesRead += pBytes; 124 notifyListener(); 125 } 126 /** Called to indicate, that a new file item has been detected. 127 */ 128 void noteItem() { 129 ++items; 130 } 131 /** Called for notifying the listener. 132 */ 133 private void notifyListener() { 134 if (listener != null) { 135 listener.update(bytesRead, contentLength, items); 136 } 137 } 138 } 139 140 // ----------------------------------------------------- Manifest constants 141 142 143 /** 144 * The Carriage Return ASCII character value. 145 */ 146 public static final byte CR = 0x0D; 147 148 149 /** 150 * The Line Feed ASCII character value. 151 */ 152 public static final byte LF = 0x0A; 153 154 155 /** 156 * The dash (-) ASCII character value. 157 */ 158 public static final byte DASH = 0x2D; 159 160 161 /** 162 * The maximum length of <code>header-part</code> that will be 163 * processed (10 kilobytes = 10240 bytes.). 164 */ 165 public static final int HEADER_PART_SIZE_MAX = 10240; 166 167 168 /** 169 * The default length of the buffer used for processing a request. 170 */ 171 protected static final int DEFAULT_BUFSIZE = 4096; 172 173 174 /** 175 * A byte sequence that marks the end of <code>header-part</code> 176 * (<code>CRLFCRLF</code>). 177 */ 178 protected static final byte[] HEADER_SEPARATOR = { 179 CR, LF, CR, LF }; 180 181 182 /** 183 * A byte sequence that that follows a delimiter that will be 184 * followed by an encapsulation (<code>CRLF</code>). 185 */ 186 protected static final byte[] FIELD_SEPARATOR = { 187 CR, LF}; 188 189 190 /** 191 * A byte sequence that that follows a delimiter of the last 192 * encapsulation in the stream (<code>--</code>). 193 */ 194 protected static final byte[] STREAM_TERMINATOR = { 195 DASH, DASH}; 196 197 198 /** 199 * A byte sequence that precedes a boundary (<code>CRLF--</code>). 200 */ 201 protected static final byte[] BOUNDARY_PREFIX = { 202 CR, LF, DASH, DASH}; 203 204 205 // ----------------------------------------------------------- Data members 206 207 208 /** 209 * The input stream from which data is read. 210 */ 211 private final InputStream input; 212 213 214 /** 215 * The length of the boundary token plus the leading <code>CRLF--</code>. 216 */ 217 private int boundaryLength; 218 219 220 /** 221 * The amount of data, in bytes, that must be kept in the buffer in order 222 * to detect delimiters reliably. 223 */ 224 private int keepRegion; 225 226 227 /** 228 * The byte sequence that partitions the stream. 229 */ 230 private byte[] boundary; 231 232 233 /** 234 * The length of the buffer used for processing the request. 235 */ 236 private final int bufSize; 237 238 239 /** 240 * The buffer used for processing the request. 241 */ 242 private final byte[] buffer; 243 244 245 /** 246 * The index of first valid character in the buffer. 247 * <br> 248 * 0 <= head < bufSize 249 */ 250 private int head; 251 252 253 /** 254 * The index of last valid characer in the buffer + 1. 255 * <br> 256 * 0 <= tail <= bufSize 257 */ 258 private int tail; 259 260 261 /** 262 * The content encoding to use when reading headers. 263 */ 264 private String headerEncoding; 265 266 267 /** 268 * The progress notifier, if any, or null. 269 */ 270 private final ProgressNotifier notifier; 271 272 // ----------------------------------------------------------- Constructors 273 274 /** 275 * Creates a new instance. 276 * @deprecated Use {@link #MultipartStream(InputStream, byte[], 277 * org.apache.commons.fileupload.MultipartStream.ProgressNotifier)}, 278 * or {@link #MultipartStream(InputStream, byte[], int, 279 * org.apache.commons.fileupload.MultipartStream.ProgressNotifier)} 280 */ 281 public MultipartStream() { 282 this(null, null, null); 283 } 284 285 /** 286 * <p> Constructs a <code>MultipartStream</code> with a custom size buffer 287 * and no progress notifier. 288 * 289 * <p> Note that the buffer must be at least big enough to contain the 290 * boundary string, plus 4 characters for CR/LF and double dash, plus at 291 * least one byte of data. Too small a buffer size setting will degrade 292 * performance. 293 * 294 * @param input The <code>InputStream</code> to serve as a data source. 295 * @param boundary The token used for dividing the stream into 296 * <code>encapsulations</code>. 297 * @param bufSize The size of the buffer to be used, in bytes. 298 * 299 * @see #MultipartStream(InputStream, byte[], 300 * MultipartStream.ProgressNotifier) 301 * @deprecated Use {@link #MultipartStream(InputStream, byte[], int, 302 * org.apache.commons.fileupload.MultipartStream.ProgressNotifier)}. 303 */ 304 public MultipartStream(InputStream input, byte[] boundary, int bufSize) { 305 this(input, boundary, bufSize, null); 306 } 307 308 /** 309 * <p> Constructs a <code>MultipartStream</code> with a custom size buffer. 310 * 311 * <p> Note that the buffer must be at least big enough to contain the 312 * boundary string, plus 4 characters for CR/LF and double dash, plus at 313 * least one byte of data. Too small a buffer size setting will degrade 314 * performance. 315 * 316 * @param input The <code>InputStream</code> to serve as a data source. 317 * @param boundary The token used for dividing the stream into 318 * <code>encapsulations</code>. 319 * @param bufSize The size of the buffer to be used, in bytes. 320 * @param pNotifier The notifier, which is used for calling the 321 * progress listener, if any. 322 * 323 * @see #MultipartStream(InputStream, byte[], 324 * MultipartStream.ProgressNotifier) 325 */ 326 MultipartStream(InputStream input, 327 byte[] boundary, 328 int bufSize, 329 ProgressNotifier pNotifier) { 330 this.input = input; 331 this.bufSize = bufSize; 332 this.buffer = new byte[bufSize]; 333 this.notifier = pNotifier; 334 335 // We prepend CR/LF to the boundary to chop trailng CR/LF from 336 // body-data tokens. 337 this.boundary = new byte[boundary.length + BOUNDARY_PREFIX.length]; 338 this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length; 339 this.keepRegion = this.boundary.length; 340 System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, 341 BOUNDARY_PREFIX.length); 342 System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, 343 boundary.length); 344 345 head = 0; 346 tail = 0; 347 } 348 349 350 /** 351 * <p> Constructs a <code>MultipartStream</code> with a default size buffer. 352 * 353 * @param input The <code>InputStream</code> to serve as a data source. 354 * @param boundary The token used for dividing the stream into 355 * <code>encapsulations</code>. 356 * @param pNotifier An object for calling the progress listener, if any. 357 * 358 * 359 * @see #MultipartStream(InputStream, byte[], int, 360 * MultipartStream.ProgressNotifier) 361 */ 362 MultipartStream(InputStream input, 363 byte[] boundary, 364 ProgressNotifier pNotifier) { 365 this(input, boundary, DEFAULT_BUFSIZE, pNotifier); 366 } 367 368 /** 369 * <p> Constructs a <code>MultipartStream</code> with a default size buffer. 370 * 371 * @param input The <code>InputStream</code> to serve as a data source. 372 * @param boundary The token used for dividing the stream into 373 * <code>encapsulations</code>. 374 * 375 * @deprecated Use {@link #MultipartStream(InputStream, byte[], 376 * MultipartStream.ProgressNotifier)}. 377 * @see #MultipartStream(InputStream, byte[], int, 378 * MultipartStream.ProgressNotifier) 379 */ 380 public MultipartStream(InputStream input, 381 byte[] boundary) { 382 this(input, boundary, DEFAULT_BUFSIZE, null); 383 } 384 385 // --------------------------------------------------------- Public methods 386 387 388 /** 389 * Retrieves the character encoding used when reading the headers of an 390 * individual part. When not specified, or <code>null</code>, the platform 391 * default encoding is used. 392 393 * 394 * @return The encoding used to read part headers. 395 */ 396 public String getHeaderEncoding() { 397 return headerEncoding; 398 } 399 400 401 /** 402 * Specifies the character encoding to be used when reading the headers of 403 * individual parts. When not specified, or <code>null</code>, the platform 404 * default encoding is used. 405 * 406 * @param encoding The encoding used to read part headers. 407 */ 408 public void setHeaderEncoding(String encoding) { 409 headerEncoding = encoding; 410 } 411 412 413 /** 414 * Reads a byte from the <code>buffer</code>, and refills it as 415 * necessary. 416 * 417 * @return The next byte from the input stream. 418 * 419 * @throws IOException if there is no more data available. 420 */ 421 public byte readByte() throws IOException { 422 // Buffer depleted ? 423 if (head == tail) { 424 head = 0; 425 // Refill. 426 tail = input.read(buffer, head, bufSize); 427 if (tail == -1) { 428 // No more data available. 429 throw new IOException("No more data is available"); 430 } 431 if (notifier != null) { 432 notifier.noteBytesRead(tail); 433 } 434 } 435 return buffer[head++]; 436 } 437 438 439 /** 440 * Skips a <code>boundary</code> token, and checks whether more 441 * <code>encapsulations</code> are contained in the stream. 442 * 443 * @return <code>true</code> if there are more encapsulations in 444 * this stream; <code>false</code> otherwise. 445 * 446 * @throws MalformedStreamException if the stream ends unexpecetedly or 447 * fails to follow required syntax. 448 */ 449 public boolean readBoundary() 450 throws MalformedStreamException { 451 byte[] marker = new byte[2]; 452 boolean nextChunk = false; 453 454 head += boundaryLength; 455 try { 456 marker[0] = readByte(); 457 if (marker[0] == LF) { 458 // Work around IE5 Mac bug with input type=image. 459 // Because the boundary delimiter, not including the trailing 460 // CRLF, must not appear within any file (RFC 2046, section 461 // 5.1.1), we know the missing CR is due to a buggy browser 462 // rather than a file containing something similar to a 463 // boundary. 464 return true; 465 } 466 467 marker[1] = readByte(); 468 if (arrayequals(marker, STREAM_TERMINATOR, 2)) { 469 nextChunk = false; 470 } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) { 471 nextChunk = true; 472 } else { 473 throw new MalformedStreamException( 474 "Unexpected characters follow a boundary"); 475 } 476 } catch (IOException e) { 477 throw new MalformedStreamException("Stream ended unexpectedly"); 478 } 479 return nextChunk; 480 } 481 482 483 /** 484 * <p>Changes the boundary token used for partitioning the stream. 485 * 486 * <p>This method allows single pass processing of nested multipart 487 * streams. 488 * 489 * <p>The boundary token of the nested stream is <code>required</code> 490 * to be of the same length as the boundary token in parent stream. 491 * 492 * <p>Restoring the parent stream boundary token after processing of a 493 * nested stream is left to the application. 494 * 495 * @param boundary The boundary to be used for parsing of the nested 496 * stream. 497 * 498 * @throws IllegalBoundaryException if the <code>boundary</code> 499 * has a different length than the one 500 * being currently parsed. 501 */ 502 public void setBoundary(byte[] boundary) 503 throws IllegalBoundaryException { 504 if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) { 505 throw new IllegalBoundaryException( 506 "The length of a boundary token can not be changed"); 507 } 508 System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, 509 boundary.length); 510 } 511 512 513 /** 514 * <p>Reads the <code>header-part</code> of the current 515 * <code>encapsulation</code>. 516 * 517 * <p>Headers are returned verbatim to the input stream, including the 518 * trailing <code>CRLF</code> marker. Parsing is left to the 519 * application. 520 * 521 * <p><strong>TODO</strong> allow limiting maximum header size to 522 * protect against abuse. 523 * 524 * @return The <code>header-part</code> of the current encapsulation. 525 * 526 * @throws MalformedStreamException if the stream ends unexpecetedly. 527 */ 528 public String readHeaders() 529 throws MalformedStreamException { 530 int i = 0; 531 byte b; 532 // to support multi-byte characters 533 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 534 int size = 0; 535 while (i < HEADER_SEPARATOR.length) { 536 try { 537 b = readByte(); 538 } catch (IOException e) { 539 throw new MalformedStreamException("Stream ended unexpectedly"); 540 } 541 if (++size > HEADER_PART_SIZE_MAX) { 542 throw new MalformedStreamException( 543 "Header section has more than " + HEADER_PART_SIZE_MAX 544 + " bytes (maybe it is not properly terminated)"); 545 } 546 if (b == HEADER_SEPARATOR[i]) { 547 i++; 548 } else { 549 i = 0; 550 } 551 baos.write(b); 552 } 553 554 String headers = null; 555 if (headerEncoding != null) { 556 try { 557 headers = baos.toString(headerEncoding); 558 } catch (UnsupportedEncodingException e) { 559 // Fall back to platform default if specified encoding is not 560 // supported. 561 headers = baos.toString(); 562 } 563 } else { 564 headers = baos.toString(); 565 } 566 567 return headers; 568 } 569 570 571 /** 572 * <p>Reads <code>body-data</code> from the current 573 * <code>encapsulation</code> and writes its contents into the 574 * output <code>Stream</code>. 575 * 576 * <p>Arbitrary large amounts of data can be processed by this 577 * method using a constant size buffer. (see {@link 578 * #MultipartStream(InputStream,byte[],int, 579 * MultipartStream.ProgressNotifier) constructor}). 580 * 581 * @param output The <code>Stream</code> to write data into. May 582 * be null, in which case this method is equivalent 583 * to {@link #discardBodyData()}. 584 * 585 * @return the amount of data written. 586 * 587 * @throws MalformedStreamException if the stream ends unexpectedly. 588 * @throws IOException if an i/o error occurs. 589 */ 590 public int readBodyData(OutputStream output) 591 throws MalformedStreamException, IOException { 592 final InputStream istream = newInputStream(); 593 return (int) Streams.copy(istream, output, false); 594 } 595 596 /** 597 * Creates a new {@link ItemInputStream}. 598 * @return A new instance of {@link ItemInputStream}. 599 */ 600 ItemInputStream newInputStream() { 601 return new ItemInputStream(); 602 } 603 604 /** 605 * <p> Reads <code>body-data</code> from the current 606 * <code>encapsulation</code> and discards it. 607 * 608 * <p>Use this method to skip encapsulations you don't need or don't 609 * understand. 610 * 611 * @return The amount of data discarded. 612 * 613 * @throws MalformedStreamException if the stream ends unexpectedly. 614 * @throws IOException if an i/o error occurs. 615 */ 616 public int discardBodyData() 617 throws MalformedStreamException, 618 IOException { 619 return readBodyData(null); 620 } 621 622 623 /** 624 * Finds the beginning of the first <code>encapsulation</code>. 625 * 626 * @return <code>true</code> if an <code>encapsulation</code> was found in 627 * the stream. 628 * 629 * @throws IOException if an i/o error occurs. 630 */ 631 public boolean skipPreamble() 632 throws IOException { 633 // First delimiter may be not preceeded with a CRLF. 634 System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2); 635 boundaryLength = boundary.length - 2; 636 try { 637 // Discard all data up to the delimiter. 638 discardBodyData(); 639 640 // Read boundary - if succeded, the stream contains an 641 // encapsulation. 642 return readBoundary(); 643 } catch (MalformedStreamException e) { 644 return false; 645 } finally { 646 // Restore delimiter. 647 System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2); 648 boundaryLength = boundary.length; 649 boundary[0] = CR; 650 boundary[1] = LF; 651 } 652 } 653 654 655 /** 656 * Compares <code>count</code> first bytes in the arrays 657 * <code>a</code> and <code>b</code>. 658 * 659 * @param a The first array to compare. 660 * @param b The second array to compare. 661 * @param count How many bytes should be compared. 662 * 663 * @return <code>true</code> if <code>count</code> first bytes in arrays 664 * <code>a</code> and <code>b</code> are equal. 665 */ 666 public static boolean arrayequals(byte[] a, 667 byte[] b, 668 int count) { 669 for (int i = 0; i < count; i++) { 670 if (a[i] != b[i]) { 671 return false; 672 } 673 } 674 return true; 675 } 676 677 678 /** 679 * Searches for a byte of specified value in the <code>buffer</code>, 680 * starting at the specified <code>position</code>. 681 * 682 * @param value The value to find. 683 * @param pos The starting position for searching. 684 * 685 * @return The position of byte found, counting from beginning of the 686 * <code>buffer</code>, or <code>-1</code> if not found. 687 */ 688 protected int findByte(byte value, 689 int pos) { 690 for (int i = pos; i < tail; i++) { 691 if (buffer[i] == value) { 692 return i; 693 } 694 } 695 696 return -1; 697 } 698 699 700 /** 701 * Searches for the <code>boundary</code> in the <code>buffer</code> 702 * region delimited by <code>head</code> and <code>tail</code>. 703 * 704 * @return The position of the boundary found, counting from the 705 * beginning of the <code>buffer</code>, or <code>-1</code> if 706 * not found. 707 */ 708 protected int findSeparator() { 709 int first; 710 int match = 0; 711 int maxpos = tail - boundaryLength; 712 for (first = head; 713 (first <= maxpos) && (match != boundaryLength); 714 first++) { 715 first = findByte(boundary[0], first); 716 if (first == -1 || (first > maxpos)) { 717 return -1; 718 } 719 for (match = 1; match < boundaryLength; match++) { 720 if (buffer[first + match] != boundary[match]) { 721 break; 722 } 723 } 724 } 725 if (match == boundaryLength) { 726 return first - 1; 727 } 728 return -1; 729 } 730 731 /** 732 * Thrown to indicate that the input stream fails to follow the 733 * required syntax. 734 */ 735 public static class MalformedStreamException 736 extends IOException { 737 /** 738 * Constructs a <code>MalformedStreamException</code> with no 739 * detail message. 740 */ 741 public MalformedStreamException() { 742 super(); 743 } 744 745 /** 746 * Constructs an <code>MalformedStreamException</code> with 747 * the specified detail message. 748 * 749 * @param message The detail message. 750 */ 751 public MalformedStreamException(String message) { 752 super(message); 753 } 754 } 755 756 757 /** 758 * Thrown upon attempt of setting an invalid boundary token. 759 */ 760 public static class IllegalBoundaryException 761 extends IOException { 762 /** 763 * Constructs an <code>IllegalBoundaryException</code> with no 764 * detail message. 765 */ 766 public IllegalBoundaryException() { 767 super(); 768 } 769 770 /** 771 * Constructs an <code>IllegalBoundaryException</code> with 772 * the specified detail message. 773 * 774 * @param message The detail message. 775 */ 776 public IllegalBoundaryException(String message) { 777 super(message); 778 } 779 } 780 781 /** 782 * An {@link InputStream} for reading an items contents. 783 */ 784 public class ItemInputStream extends InputStream implements Closeable { 785 /** The number of bytes, which have been read so far. 786 */ 787 private long total; 788 /** The number of bytes, which must be hold, because 789 * they might be a part of the boundary. 790 */ 791 private int pad; 792 /** The current offset in the buffer. 793 */ 794 private int pos; 795 /** Whether the stream is already closed. 796 */ 797 private boolean closed; 798 799 /** 800 * Creates a new instance. 801 */ 802 ItemInputStream() { 803 findSeparator(); 804 } 805 806 /** 807 * Called for finding the separator. 808 */ 809 private void findSeparator() { 810 pos = MultipartStream.this.findSeparator(); 811 if (pos == -1) { 812 if (tail - head > keepRegion) { 813 pad = keepRegion; 814 } else { 815 pad = tail - head; 816 } 817 } 818 } 819 820 /** 821 * Returns the number of bytes, which have been read 822 * by the stream. 823 * @return Number of bytes, which have been read so far. 824 */ 825 public long getBytesRead() { 826 return total; 827 } 828 829 /** 830 * Returns the number of bytes, which are currently 831 * available, without blocking. 832 * @throws IOException An I/O error occurs. 833 * @return Number of bytes in the buffer. 834 */ 835 public int available() throws IOException { 836 if (pos == -1) { 837 return tail - head - pad; 838 } 839 return pos - head; 840 } 841 842 /** Offset when converting negative bytes to integers. 843 */ 844 private static final int BYTE_POSITIVE_OFFSET = 256; 845 846 /** 847 * Returns the next byte in the stream. 848 * @return The next byte in the stream, as a non-negative 849 * integer, or -1 for EOF. 850 * @throws IOException An I/O error occurred. 851 */ 852 public int read() throws IOException { 853 if (closed) { 854 throw new FileItemStream.ItemSkippedException(); 855 } 856 if (available() == 0) { 857 if (makeAvailable() == 0) { 858 return -1; 859 } 860 } 861 ++total; 862 int b = buffer[head++]; 863 if (b >= 0) { 864 return b; 865 } 866 return b + BYTE_POSITIVE_OFFSET; 867 } 868 869 /** 870 * Reads bytes into the given buffer. 871 * @param b The destination buffer, where to write to. 872 * @param off Offset of the first byte in the buffer. 873 * @param len Maximum number of bytes to read. 874 * @return Number of bytes, which have been actually read, 875 * or -1 for EOF. 876 * @throws IOException An I/O error occurred. 877 */ 878 public int read(byte[] b, int off, int len) throws IOException { 879 if (closed) { 880 throw new FileItemStream.ItemSkippedException(); 881 } 882 if (len == 0) { 883 return 0; 884 } 885 int res = available(); 886 if (res == 0) { 887 res = makeAvailable(); 888 if (res == 0) { 889 return -1; 890 } 891 } 892 res = Math.min(res, len); 893 System.arraycopy(buffer, head, b, off, res); 894 head += res; 895 total += res; 896 return res; 897 } 898 899 /** 900 * Closes the input stream. 901 * @throws IOException An I/O error occurred. 902 */ 903 public void close() throws IOException { 904 close(false); 905 } 906 907 /** 908 * Closes the input stream. 909 * @param pCloseUnderlying Whether to close the underlying stream 910 * (hard close) 911 * @throws IOException An I/O error occurred. 912 */ 913 public void close(boolean pCloseUnderlying) throws IOException { 914 if (closed) { 915 return; 916 } 917 if (pCloseUnderlying) { 918 closed = true; 919 input.close(); 920 } else { 921 for (;;) { 922 int av = available(); 923 if (av == 0) { 924 av = makeAvailable(); 925 if (av == 0) { 926 break; 927 } 928 } 929 skip(av); 930 } 931 } 932 closed = true; 933 } 934 935 /** 936 * Skips the given number of bytes. 937 * @param bytes Number of bytes to skip. 938 * @return The number of bytes, which have actually been 939 * skipped. 940 * @throws IOException An I/O error occurred. 941 */ 942 public long skip(long bytes) throws IOException { 943 if (closed) { 944 throw new FileItemStream.ItemSkippedException(); 945 } 946 int av = available(); 947 if (av == 0) { 948 av = makeAvailable(); 949 if (av == 0) { 950 return 0; 951 } 952 } 953 long res = Math.min(av, bytes); 954 head += res; 955 return res; 956 } 957 958 /** 959 * Attempts to read more data. 960 * @return Number of available bytes 961 * @throws IOException An I/O error occurred. 962 */ 963 private int makeAvailable() throws IOException { 964 if (pos != -1) { 965 return 0; 966 } 967 968 // Move the data to the beginning of the buffer. 969 total += tail - head - pad; 970 System.arraycopy(buffer, tail - pad, buffer, 0, pad); 971 972 // Refill buffer with new data. 973 head = 0; 974 tail = pad; 975 976 for (;;) { 977 int bytesRead = input.read(buffer, tail, bufSize - tail); 978 if (bytesRead == -1) { 979 // The last pad amount is left in the buffer. 980 // Boundary can't be in there so signal an error 981 // condition. 982 final String msg = "Stream ended unexpectedly"; 983 throw new MalformedStreamException(msg); 984 } 985 if (notifier != null) { 986 notifier.noteBytesRead(bytesRead); 987 } 988 tail += bytesRead; 989 990 findSeparator(); 991 int av = available(); 992 993 if (av > 0 || pos != -1) { 994 return av; 995 } 996 } 997 } 998 999 /** 1000 * Returns, whether the stream is closed. 1001 * @return True, if the stream is closed, otherwise false. 1002 */ 1003 public boolean isClosed() { 1004 return closed; 1005 } 1006 } 1007 1008 // ------------------------------------------------------ Debugging methods 1009 1010 1011 // These are the methods that were used to debug this stuff. 1012 /* 1013 1014 // Dump data. 1015 protected void dump() 1016 { 1017 System.out.println("01234567890"); 1018 byte[] temp = new byte[buffer.length]; 1019 for(int i=0; i<buffer.length; i++) 1020 { 1021 if (buffer[i] == 0x0D || buffer[i] == 0x0A) 1022 { 1023 temp[i] = 0x21; 1024 } 1025 else 1026 { 1027 temp[i] = buffer[i]; 1028 } 1029 } 1030 System.out.println(new String(temp)); 1031 int i; 1032 for (i=0; i<head; i++) 1033 System.out.print(" "); 1034 System.out.println("h"); 1035 for (i=0; i<tail; i++) 1036 System.out.print(" "); 1037 System.out.println("t"); 1038 System.out.flush(); 1039 } 1040 1041 // Main routine, for testing purposes only. 1042 // 1043 // @param args A String[] with the command line arguments. 1044 // @throws Exception, a generic exception. 1045 public static void main( String[] args ) 1046 throws Exception 1047 { 1048 File boundaryFile = new File("boundary.dat"); 1049 int boundarySize = (int)boundaryFile.length(); 1050 byte[] boundary = new byte[boundarySize]; 1051 FileInputStream input = new FileInputStream(boundaryFile); 1052 input.read(boundary,0,boundarySize); 1053 1054 input = new FileInputStream("multipart.dat"); 1055 MultipartStream chunks = new MultipartStream(input, boundary); 1056 1057 int i = 0; 1058 String header; 1059 OutputStream output; 1060 boolean nextChunk = chunks.skipPreamble(); 1061 while (nextChunk) 1062 { 1063 header = chunks.readHeaders(); 1064 System.out.println("!"+header+"!"); 1065 System.out.println("wrote part"+i+".dat"); 1066 output = new FileOutputStream("part"+(i++)+".dat"); 1067 chunks.readBodyData(output); 1068 nextChunk = chunks.readBoundary(); 1069 } 1070 } 1071 1072 */ 1073 }