源码(五)

来源:互联网 发布:js一切皆对象 编辑:程序博客网 时间:2024/05/29 11:36

一.概述

1.用于处理文件上传的低级API
2.此类可用于处理符合<a href="http://www.ietf.org/rfc/rfc1867.txt"> RFC 1867中定义的MIME'multipart'格式的数据流。  可以在恒定内存使用情况下处理流中任意大量的数据
3.流的格式定义如下:
multipart-body := preamble 1*encapsulation close-delimiter epilogue<br>encapsulation := delimiter body CRLF<br>delimiter := "--" boundary CRLF<br>close-delimiter := "--" boudary "--"<br>preamble := <ignore><br>epilogue := <ignore><br>body := header-part CRLF body-part<br>header-part := 1*header CRLF<br>header := header-name ":" header-value<br> header-name := <printable ascii characters except ":"><br>header-value := <any ascii characters except CR & LF><br>body-data := <arbitrary data><br>
4.此类的一个实例
try {MultipartStream multipartStream = new MultipartStream(input, boundary);boolean nextPart = multipartStream.skipPreamble();OutputStream output;while(nextPart) {header = chunks.readHeader();// process headers create some output streammultipartStream.readBodyPart(output);nextPart = multipartStream.readBoundary();}} catch(MultipartStream.MalformedStreamException e) {// the stream failed to follow required syntax} catch(IOException) {// a read or write error occurred}

二.类结构




三.内部类 - ItemInputStream

表示一个FileItem对应stream,当FileItem是上传文件时,通过对应得ItemInputStream实例将上传文件缓存到本地磁盘
  // 用于读取item内容分类InputStream    public class ItemInputStream extends InputStream implements Closeable {        // 到目前为止已经读取的字节数        private long total;// 必须保持的字节数,因为它们可能是boundary的一部分,        private int pad;// 在缓冲区中当前偏移量        private int pos;// 流是否已经关闭了        private boolean closed;        ItemInputStream() {            findSeparator();        }        // 查找分割字节数组        private void findSeparator() {// 分割流的字节序列在缓冲区的起始位置            pos = MultipartStream.this.findSeparator();            if (pos == -1) {// 没有找到分割字节数组                if (tail - head > keepRegion) {// 缓冲区内剩余的字节数大于缓冲区必须保留的字节数                    pad = keepRegion;                } else {                    pad = tail - head;                }            }        }        /**         * 返回流已读取的字节数。         */        public long getBytesRead() {            return total;        }        /** * 返回缓冲区中的字节数 * tail:缓冲区中最大可用索引+1 * head:缓冲区下一个读取的索引 * pad: 缓冲区中保留字节数, 可能是分隔符的一部分         */        public int available() throws IOException {            if (pos == -1) {// 为-1表示当前缓冲区中无法确定当前上传文件内容的结尾                return tail - head - pad;            }            return pos - head;        }        /** Offset when converting negative bytes to integers.         */        private static final int BYTE_POSITIVE_OFFSET = 256;        /** * 返回流中的下一个字节,是一个非负整数,或读取到EOF时为-1         */        public int read() throws IOException {            if (closed) {// 流关闭了                throw new FileItemStream.ItemSkippedException();            }            if (available() == 0) {// 缓冲区中没有数据可读了                if (makeAvailable() == 0) {                    return -1;                }            }            ++total;            int b = buffer[head++];            if (b >= 0) {                return b;            }            return b + BYTE_POSITIVE_OFFSET;        }        /** * org.apache.commons.fileupload.MultipartStream.ItemInputStream         * 从缓冲区中读取上传文件内容,读到缓冲区b中         * @param b 目的缓冲区         * @param off 偏移量         * @param len 读取的字节数         * @return 实际读取的字节数,或为-1         */        public int read(byte[] b, int off, int len) throws IOException {            if (closed) {                throw new FileItemStream.ItemSkippedException();            }            if (len == 0) {                return 0;            }// 返回缓冲区中的字节数            int res = available();            if (res == 0) {// 缓冲区没有数据了// 尝试从流中读取更多数据                res = makeAvailable();                if (res == 0) {                    return -1;                }            }            res = Math.min(res, len);            System.arraycopy(buffer, head, b, off, res);            head += res;            total += res;            return res;        }        /**         * Skips the given number of bytes.         * @param bytes Number of bytes to skip.         * @return The number of bytes, which have actually been         *   skipped.         * @throws IOException An I/O error occurred.         */        public long skip(long bytes) throws IOException {            if (closed) {                throw new FileItemStream.ItemSkippedException();            }            int av = available();            if (av == 0) {                av = makeAvailable();                if (av == 0) {                    return 0;                }            }            long res = Math.min(av, bytes);            head += res;            return res;        }        /**         * 尝试从流中读取更多数据 * 为了防止当前缓冲区结尾中字节是分隔符的一部分,所以保留了42字节,因为分隔符长度为42, * 所以再次从输入流中读取上传文件内容前,需要将保留的42个字节移动到缓冲区buffer的开头,即0-41处, * 再将从输入流读取的字节添加到缓冲区buffer中         * @return Number of available bytes         */        private int makeAvailable() throws IOException {            if (pos != -1) {// pos有值表示缓冲区中存在分隔符,索引pos就是结尾,从索引pos+1开始是分隔符                return 0;            }            // 将缓冲区后保留的字节移动到缓冲区buffer前面            total += tail - head - pad;            System.arraycopy(buffer, tail - pad, buffer, 0, pad);            // 重新从输入流读取内容前重置属性,pad是缓冲区保留的字节数            head = 0;            tail = pad;            for (;;) {                int bytesRead = input.read(buffer, tail, bufSize - tail);                if (bytesRead == -1) {                    // The last pad amount is left in the buffer.                    // Boundary can't be in there so signal an error                    // condition.                    final String msg = "Stream ended unexpectedly";                    throw new MalformedStreamException(msg);                }                if (notifier != null) {                    notifier.noteBytesRead(bytesRead);                }                tail += bytesRead;                findSeparator();                int av = available();                if (av > 0 || pos != -1) {                    return av;                }            }        }        // 关闭输入流        public void close() throws IOException {            close(false);        }        /**         * 关闭输入流         * @param pCloseUnderlying 是否直接关闭底层流,硬关闭         */        public void close(boolean pCloseUnderlying) throws IOException {            if (closed) {                return;            }            if (pCloseUnderlying) {// 直接关闭底层流,硬关闭                closed = true;                input.close();            } else {// 读完,关闭                for (;;) {                    int av = available();                    if (av == 0) {                        av = makeAvailable();                        if (av == 0) {                            break;                        }                    }                    skip(av);                }            }            closed = true;        }        // 返回流是否被关闭了        public boolean isClosed() {            return closed;        }    }

四.MultipartStream

此类用于解析请求报文中的实体部分,按序处理上传文件
package org.apache.commons.fileupload;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.UnsupportedEncodingException;import org.apache.commons.fileupload.util.Closeable;import org.apache.commons.fileupload.util.Streams;/** * 用于处理文件上传的低级API * * 该类可用于处理符合<a href="http://www.ietf.org/rfc/rfc1867.txt"> RFC 1867 </a>中定义的MIME'multipart'格式的数据流。  * 可以在恒定内存使用情况下处理流中任意大量的数据 * * 流的格式以下列方式定义: *  multipart-body := preamble 1*encapsulation close-delimiter epilogue *   encapsulation := delimiter body CRLF *   delimiter := "--" boundary CRLF *   close-delimiter := "--" boudary "--" *   preamble := <ignore> *   epilogue := <ignore> *   body := header-part CRLF body-part *   header-part := 1*header CRLF *   header := header-name ":" header-value *   header-name := <printable ascii characters except ":"> *   header-value := <any ascii characters except CR & LF> *   body-data := <arbitrary data> * *   body-data可以包含另一个mulipart实体。 对这种嵌套流的单程处理的支持有限。  *   嵌套流需要具有与父流相同长度的边界boundary(参阅#setBoundary(byte [])) */public class MultipartStream {    // 内部类,用于调用进度监听器ProgressListener    static class ProgressNotifier {        // 进度监听器        private final ProgressListener listener;   // 预期字节数,或为-1        private final long contentLength;// 目前为止已经读取的字节数        private long bytesRead;        // 到目前为止读过的Item数        private int items;        /**          * @param pListener          * @param pContentLength 预期内容长度         */        ProgressNotifier(ProgressListener pListener, long pContentLength) {            listener = pListener;            contentLength = pContentLength;        }        /**          * @param pBytes 已读取的字节数         */        void noteBytesRead(int pBytes) {            bytesRead += pBytes;            notifyListener();        }        void noteItem() {            ++items;        }        private void notifyListener() {            if (listener != null) {                listener.update(bytesRead, contentLength, items);            }        }    }    // 回车符的ASCII码    public static final byte CR = 0x0D;    // 换行符的ASCII码    public static final byte LF = 0x0A;    // 破折号的ASCII码    public static final byte DASH = 0x2D;// 被处理的header-part的最大长度,10kb=10240b    public static final int HEADER_PART_SIZE_MAX = 10240;    // 缓冲区的默认大小    protected static final int DEFAULT_BUFSIZE = 4096;// CRLFCRLF是标记header-part结尾的字节序列    protected static final byte[] HEADER_SEPARATOR = {CR, LF, CR, LF };    // CRLF - 字段分隔符    protected static final byte[] FIELD_SEPARATOR = {CR, LF};    // -- 表示流的结尾    protected static final byte[] STREAM_TERMINATOR = {DASH, DASH};// CRLF-- : 一个边界boundary之前的字节序列    protected static final byte[] BOUNDARY_PREFIX = {CR, LF, DASH, DASH};    // 上传输入流    private final InputStream input;// 边界boundary长度加上CRLF--的4字节    private int boundaryLength;// 缓冲区中必须保留的数据量(以字节为单位),以便可靠地检测分隔符。    private int keepRegion;// 分割流的字节序列private byte[] boundary;// 缓冲区的大小    private final int bufSize;// 缓冲区    private final byte[] buffer;    /**     * 缓冲区中下一个读取的索引     * 0 <= head < bufSize     */    private int head;    /**     * 缓冲区中最后一个有效字符的索引+1     * 0 <= tail <= bufSize     */    private int tail;// 字符编码,如UTF-8    private String headerEncoding;    // 进度调度器,或为null    private final ProgressNotifier notifier;// 已废弃    public MultipartStream() {        this(null, null, null);    }// 已废弃    public MultipartStream(InputStream input, byte[] boundary, int bufSize) {        this(input, boundary, bufSize, null);    }// 已废弃    public MultipartStream(InputStream input, byte[] boundary) {        this(input, boundary, DEFAULT_BUFSIZE, null);    }    MultipartStream(InputStream input, byte[] boundary, ProgressNotifier pNotifier) {        this(input, boundary, DEFAULT_BUFSIZE, pNotifier);    }    /**     * <p> Constructs a <code>MultipartStream</code> with a custom size buffer.     *     * 缓冲区必须至少足够大,才能包含边界字符串,加上CR / LF和双划线的4个字符以及至少一个字节的数据。  * 缓冲区大小设置太小会降低性能。     *     * @param input    上传文件输入流     * @param boundary 用于分割流的边界, 如:[45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66, 111, 117, 110, 100, 97, 114, 121, 49, 65, 122, 89, 104, 78, 48, 50, 110, 48, 48, 109, 89, 78, 84, 120]     * @param bufSize  用于缓存的大小,单位为字节     * @param pNotifier 通知器,用于调用进度监听器     */    MultipartStream(InputStream input, byte[] boundary, int bufSize, ProgressNotifier pNotifier) {        this.input = input;        this.bufSize = bufSize;        this.buffer = new byte[bufSize];        this.notifier = pNotifier;        // BOUNDARY_PREFIX = [CR, LF, DASH, DASH]        this.boundary = new byte[boundary.length + BOUNDARY_PREFIX.length];        this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length;        this.keepRegion = this.boundary.length;        System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, BOUNDARY_PREFIX.length);        System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length);//      boundary:                [45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66, 111, 117, 110, 100, 97, 114, 121, 49, 65, 122, 89, 104, 78, 48, 50, 110, 48, 48, 109, 89, 78, 84, 120]// this.boundary:[13, 10, 45, 45, 45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66, 111, 117, 110, 100, 97, 114, 121, 49, 65, 122, 89, 104, 78, 48, 50, 110, 48, 48, 109, 89, 78, 84, 120]// 头        head = 0;// 尾        tail = 0;    }    /** * 从缓冲区中读取一个字节,并根据需要重新填充     * @return 返回输入流的下一个字节     */    public byte readByte() throws IOException {        if (head == tail) {// 缓冲区已经读完了,重新填充// 重新填充后从0开始            head = 0;            // 重新填充,从输入流中读取bufSize字节,填满缓冲区,返回读取的字节数            tail = input.read(buffer, head, bufSize);            if (tail == -1) {// -1表示流已经读取完成了,没有更多数据了                throw new IOException("No more data is available");            }            if (notifier != null) {// 进度                notifier.noteBytesRead(tail);            }        }// 从缓冲区中返回索引head处的值        return buffer[head++];    }    /** * 在discardBodyData()后调用readBoundary,能执行此方法,说明discardBodyData()时找到了分隔符 * 所以head += boundaryLength,head直接跳过分隔符,判断分隔符后2个直接 * 1.分隔符boundary后紧随CRLF(即\r\n),表示有下一个部分 * 2.分隔符boundary后紧随--(即双破折号),表示实体的结尾     * @return true表示有下一个部分,false表示没有     */    public boolean readBoundary() throws MalformedStreamException {        byte[] marker = new byte[2];        boolean nextChunk = false;// 跳过boundary        head += boundaryLength;        try {            marker[0] = readByte();// 从缓冲区读取一个字节            if (marker[0] == LF) {// 一个浏览器BUG - 造成一个边界分隔符boundary缺少CR                return true;            }            marker[1] = readByte();// 从缓冲区读取下一个字节            if (arrayequals(marker, STREAM_TERMINATOR, 2)) {// 流读取完成,TREAM_TERMINATOR = {DASH, DASH}                nextChunk = false;            } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) {// CRLF表示字段分隔符,有下一部分,FIELD_SEPARATOR = {CR, LF}                nextChunk = true;            } else {                throw new MalformedStreamException("Unexpected characters follow a boundary");            }        } catch (IOException e) {            throw new MalformedStreamException("Stream ended unexpectedly");        }        return nextChunk;    }    /**     * 设置流分隔符     * @param boundary 流分隔符字节数组     */    public void setBoundary(byte[] boundary) throws IllegalBoundaryException {// BOUNDARY_PREFIX = {CR, LF, DASH, DASH}        if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) {// 流分隔符的长度是不能更改的            throw new IllegalBoundaryException("The length of a boundary token can not be changed");        }        System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length);    }    /** * 从缓冲区中检索当前部分的尸体首部,实体首部与实体主体以\r\n\r\n分割     * 例子: *------WebKitFormBoundary1AzYhN02n00mYNTx\r\n *Content-Disposition: form-data; name="fileName"; filename="123.xlsx"\r\n *Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\r\n *  \r\n *  实体主体 *  返回: *  Content-Disposition: form-data; name="fileName"; filename="123.xlsx"\r\n *Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\r\n     */    public String readHeaders() throws MalformedStreamException {        int i = 0;        byte b;        // to support multi-byte characters        ByteArrayOutputStream baos = new ByteArrayOutputStream();        int size = 0;        while (i < HEADER_SEPARATOR.length) {// header-part分隔符HEADER_SEPARATOR = {CR, LF, CR, LF }            try {// 从缓冲区读取一个字节                b = readByte();            } catch (IOException e) {                throw new MalformedStreamException("Stream ended unexpectedly");            }            if (++size > HEADER_PART_SIZE_MAX) {// HEADER_PART_SIZE_MAX表示header-part的最大长度                throw new MalformedStreamException("Header section has more than " + HEADER_PART_SIZE_MAX + " bytes (maybe it is not properly terminated)");            }            if (b == HEADER_SEPARATOR[i]) {// 一直读取到[13, 10, 13, 10](CRLFCRLF)结束或读取长度大于头部最大值                i++;            } else {                i = 0;            }            baos.write(b);        }        String headers = null;        if (headerEncoding != null) {// header编码,UTF-8            try {                headers = baos.toString(headerEncoding);            } catch (UnsupportedEncodingException e) {                // 如果不支持指定的编码,则返回到平台默认值。                headers = baos.toString();            }        } else {            headers = baos.toString();        }        return headers;    }/** * 实体中可能有多个部分,而各部分之间以分隔符boundary分隔 * 此方法从缓冲区中查找分隔符,看是否有另一个部分 */    public boolean skipPreamble() throws IOException {        // 第一个分隔符之前没有CRLF,先清除boundary字节数组中CRLF,2个字节        System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);        boundaryLength = boundary.length - 2;        try {            // 从当前的encapsulation中读取body-data,并丢弃它            discardBodyData();            // 读取边界boundary - 如果成功,说明流包含一个封装encapsulation            return readBoundary();        } catch (MalformedStreamException e) {            return false;        } finally {            //恢复分隔符            System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);            boundaryLength = boundary.length;            boundary[0] = CR;            boundary[1] = LF;        }    }    /** * 查找到下一个分隔符boundary了,就丢弃缓冲区中head之后,下一个分隔符之前的字节     * @return 返回丢弃的数据量     */    public int discardBodyData() throws MalformedStreamException, IOException {        return readBodyData(null);    }    /** * 从当前的encapsulation中读取body-data,并将其写入到输出流中     * @param output      * @return 写入的数据量     */    public int readBodyData(OutputStream output) throws MalformedStreamException, IOException {        final InputStream istream = newInputStream();        return (int) Streams.copy(istream, output, false);    }    ItemInputStream newInputStream() {        return new ItemInputStream();    }    // 比较字节数组前count位是否相等    public static boolean arrayequals(byte[] a, byte[] b, int count) {        for (int i = 0; i < count; i++) {            if (a[i] != b[i]) {                return false;            }        }        return true;    }/** * 从缓冲区的head和tail之间检索分隔符boundary * 返回分隔符boundary在缓冲区中的开始位置,-1表示没有找到分隔符 */    protected int findSeparator() {        int first;        int match = 0;// 最大的可能位置        int maxpos = tail - boundaryLength;        for (first = head; (first <= maxpos) && (match != boundaryLength); first++) {// boundary - 分割流的字节数组// 从缓冲区中查找指定的boundary[0]在缓冲区的位置            first = findByte(boundary[0], first);            if (first == -1 || (first > maxpos)) {// 没有找到                return -1;            }// 在缓冲区找到了boundary[0]            for (match = 1; match < boundaryLength; match++) {                if (buffer[first + match] != boundary[match]) {                    break;                }            }        }        if (match == boundaryLength) {// 缓冲区中存在分割流的字节数组            return first - 1;        }        return -1;    }    /** * 搜索一个指定值在缓冲区的位置,偏移量     *     * @param value The value to find.     * @param pos   从pos位开始搜索     * @return 返回value在缓冲区中的位置,-1表示没有找到     */    protected int findByte(byte value, int pos) {        for (int i = pos; i < tail; i++) {            if (buffer[i] == value) {                return i;            }        }        return -1;    }    // 用于读取item内容分类InputStream    public class ItemInputStream extends InputStream implements Closeable {        // 到目前为止已经读取的字节数        private long total;// 必须保持的字节数,因为它们可能是boundary的一部分,        private int pad;// 在缓冲区中当前偏移量        private int pos;// 流是否已经关闭了        private boolean closed;        ItemInputStream() {            findSeparator();        }        // 查找分割字节数组        private void findSeparator() {// 分割流的字节序列在缓冲区的起始位置            pos = MultipartStream.this.findSeparator();            if (pos == -1) {// 没有找到分割字节数组                if (tail - head > keepRegion) {// 缓冲区内剩余的字节数大于缓冲区必须保留的字节数                    pad = keepRegion;                } else {                    pad = tail - head;                }            }        }        /**         * 返回流已读取的字节数。         */        public long getBytesRead() {            return total;        }        /** * 返回缓冲区中的字节数 * tail:缓冲区中最大可用索引+1 * head:缓冲区下一个读取的索引 * pad: 缓冲区中保留字节数, 可能是分隔符的一部分         */        public int available() throws IOException {            if (pos == -1) {// 为-1表示当前缓冲区中无法确定当前上传文件内容的结尾                return tail - head - pad;            }            return pos - head;        }        /** Offset when converting negative bytes to integers.         */        private static final int BYTE_POSITIVE_OFFSET = 256;        /** * 返回流中的下一个字节,是一个非负整数,或读取到EOF时为-1         */        public int read() throws IOException {            if (closed) {// 流关闭了                throw new FileItemStream.ItemSkippedException();            }            if (available() == 0) {// 缓冲区中没有数据可读了                if (makeAvailable() == 0) {                    return -1;                }            }            ++total;            int b = buffer[head++];            if (b >= 0) {                return b;            }            return b + BYTE_POSITIVE_OFFSET;        }        /** * org.apache.commons.fileupload.MultipartStream.ItemInputStream         * 从缓冲区中读取上传文件内容,读到缓冲区b中         * @param b 目的缓冲区         * @param off 偏移量         * @param len 读取的字节数         * @return 实际读取的字节数,或为-1         */        public int read(byte[] b, int off, int len) throws IOException {            if (closed) {                throw new FileItemStream.ItemSkippedException();            }            if (len == 0) {                return 0;            }// 返回缓冲区中的字节数            int res = available();            if (res == 0) {// 缓冲区没有数据了// 尝试从流中读取更多数据                res = makeAvailable();                if (res == 0) {                    return -1;                }            }            res = Math.min(res, len);            System.arraycopy(buffer, head, b, off, res);            head += res;            total += res;            return res;        }        /**         * Skips the given number of bytes.         * @param bytes Number of bytes to skip.         * @return The number of bytes, which have actually been         *   skipped.         * @throws IOException An I/O error occurred.         */        public long skip(long bytes) throws IOException {            if (closed) {                throw new FileItemStream.ItemSkippedException();            }            int av = available();            if (av == 0) {                av = makeAvailable();                if (av == 0) {                    return 0;                }            }            long res = Math.min(av, bytes);            head += res;            return res;        }        /**         * 尝试从流中读取更多数据 * 为了防止当前缓冲区结尾中字节是分隔符的一部分,所以保留了42字节,因为分隔符长度为42, * 所以再次从输入流中读取上传文件内容前,需要将保留的42个字节移动到缓冲区buffer的开头,即0-41处, * 再将从输入流读取的字节添加到缓冲区buffer中         * @return Number of available bytes         */        private int makeAvailable() throws IOException {            if (pos != -1) {// pos有值表示缓冲区中存在分隔符,索引pos就是结尾,从索引pos+1开始是分隔符                return 0;            }            // 将缓冲区后保留的字节移动到缓冲区buffer前面            total += tail - head - pad;            System.arraycopy(buffer, tail - pad, buffer, 0, pad);            // 重新从输入流读取内容前重置属性,pad是缓冲区保留的字节数            head = 0;            tail = pad;            for (;;) {                int bytesRead = input.read(buffer, tail, bufSize - tail);                if (bytesRead == -1) {                    // The last pad amount is left in the buffer.                    // Boundary can't be in there so signal an error                    // condition.                    final String msg = "Stream ended unexpectedly";                    throw new MalformedStreamException(msg);                }                if (notifier != null) {                    notifier.noteBytesRead(bytesRead);                }                tail += bytesRead;                findSeparator();                int av = available();                if (av > 0 || pos != -1) {                    return av;                }            }        }        // 关闭输入流        public void close() throws IOException {            close(false);        }        /**         * 关闭输入流         * @param pCloseUnderlying 是否直接关闭底层流,硬关闭         */        public void close(boolean pCloseUnderlying) throws IOException {            if (closed) {                return;            }            if (pCloseUnderlying) {// 直接关闭底层流,硬关闭                closed = true;                input.close();            } else {// 读完,关闭                for (;;) {                    int av = available();                    if (av == 0) {                        av = makeAvailable();                        if (av == 0) {                            break;                        }                    }                    skip(av);                }            }            closed = true;        }        // 返回流是否被关闭了        public boolean isClosed() {            return closed;        }    }    /** * 读取单个部分标题时使用的字符编码 * 如果未定义或为null,就使用平台的默认编码     * @return 返回如UTF-8     */    public String getHeaderEncoding() {        return headerEncoding;    }    public void setHeaderEncoding(String encoding) {        headerEncoding = encoding;    }    /**     * Thrown to indicate that the input stream fails to follow the required syntax.     */    public static class MalformedStreamException extends IOException {        /**         * Constructs a <code>MalformedStreamException</code> with no detail message.         */        public MalformedStreamException() {            super();        }        /**         * Constructs an <code>MalformedStreamException</code> with the specified detail message.         * @param message The detail message.         */        public MalformedStreamException(String message) {            super(message);        }    }    /**     * Thrown upon attempt of setting an invalid boundary token.     */    public static class IllegalBoundaryException extends IOException {        /**         * Constructs an <code>IllegalBoundaryException</code> with no detail message.         */        public IllegalBoundaryException() {            super();        }        /**         * Constructs an <code>IllegalBoundaryException</code> with the specified detail message.         *         * @param message The detail message.         */        public IllegalBoundaryException(String message) {            super(message);        }    }    // These are the methods that were used to debug this stuff.    /*    // Dump data.    protected void dump()    {        System.out.println("01234567890");        byte[] temp = new byte[buffer.length];        for(int i=0; i<buffer.length; i++)        {            if (buffer[i] == 0x0D || buffer[i] == 0x0A)            {                temp[i] = 0x21;            }            else            {                temp[i] = buffer[i];            }        }        System.out.println(new String(temp));        int i;        for (i=0; i<head; i++)            System.out.print(" ");        System.out.println("h");        for (i=0; i<tail; i++)            System.out.print(" ");        System.out.println("t");        System.out.flush();    }    // Main routine, for testing purposes only.    //    // @param args A String[] with the command line arguments.    // @throws Exception, a generic exception.    public static void main( String[] args ) throws Exception {        File boundaryFile = new File("boundary.dat");        int boundarySize = (int)boundaryFile.length();        byte[] boundary = new byte[boundarySize];        FileInputStream input = new FileInputStream(boundaryFile);        input.read(boundary,0,boundarySize);        input = new FileInputStream("multipart.dat");        MultipartStream chunks = new MultipartStream(input, boundary);        int i = 0;        String header;        OutputStream output;        boolean nextChunk = chunks.skipPreamble();        while (nextChunk) {            header = chunks.readHeaders();            System.out.println("!"+header+"!");            System.out.println("wrote part"+i+".dat");            output = new FileOutputStream("part"+(i++)+".dat");            chunks.readBodyData(output);            nextChunk = chunks.readBoundary();        }    }     */}