源码(五) - MultipartStream

一.概述

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 stream
		multipartStream.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();
        }
    }

     */
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值