由一個下載文件數據丟失問題分析BufferedOutputStream源碼

今天忽然遇到一個問題,經過下載文件的接口下載的文件比實際文件小了2kb,並且文件中的內容比實際內容少了不少,帶着這個問題,我跟蹤代碼執行流程,咱們來看一下核心代碼:
bis = new BufferedInputStream(new FileInputStream(targetFilePath));
        bos = new BufferedOutputStream(response.getOutputStream());
        byte[] buff = new byte[2048];
        int bytesRead;
        while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
            bos.write(buff, 0, bytesRead);
        }

核心代碼很是簡單,就是根據目標文件,經過FileInputStream流來讀取目標文件流,寫入到response的輸出流中。中間經過BufferedInputStream和BufferedOutputStream緩衝流來提升性能。 根據上述代碼,我大膽猜想可能出現問題的緣由是BufferedInputStream或者BufferedOutputStream。進一步分析:java

while (-1 != (bytesRead = bis.read(buff, 0, buff.length)))

該語句是循環讀取全部讀緩衝區的內容,所以,該語句出現問題的概率不是很大,很大多是由於寫緩衝區的問題,下面我經過分析BufferedOutputStream的源碼來看看能不能找出問題的緣由:性能

BufferedOutputStream位於 java.io包下
	
	/**
	* 繼承自FilterOutputStream(FilterOutputStream有一個			OutputStream的屬性,就是目標輸出out對象流)
	*/
	public class BufferedOutputStream extends FilterOutputStream {
	    /**
		 * 用來存儲數據的緩衝區(默認8912個字節)
		 */
		protected byte buf[];

		/**
		 * 當前已存儲數據的字節數(我的理解爲指向已存儲數據末尾的一個指針)
		 */
		protected int count;
		
		/**
		* 構造方法1: 用設置目標輸出流對象,同時默認buff緩衝區大小8912個字節
		*/
		 public BufferedOutputStream(OutputStream out) {
			this(out, 8192);
		}
		
		/**
		* 構造方法2:設置輸出流對象,自定義緩衝區的大小,
		*/
		 public BufferedOutputStream(OutputStream out, int size) {
			super(out);
			if (size <= 0) {
				throw new IllegalArgumentException("Buffer size <= 0");
				}
			buf = new byte[size];
		}
		
		
			/**
			* 刷新緩衝區(將緩衝區內容寫入到目標流對象中,同同時將count置爲0)
			**/
		   private void flushBuffer() throws IOException {
   			 if (count > 0) {
        			out.write(buf, 0, count);
        			count = 0;
    			}
		}
		
		/**
		* 向緩衝區寫一個字節數據
		**/
		public synchronized void write(int b) throws IOException {
			//先判斷緩衝區是否已滿,若是已滿,清空緩衝區
			if (count >= buf.length) {
       			 	flushBuffer();
    			}
    			buf[count++] = (byte)b;
		}
		
		
		/**
		* 向緩衝區寫入指定長度的數據
		**/
		    public synchronized void write(byte b[], int off, int len) throws IOException {
			//判斷寫入數據的長度是否超過緩衝區大小,若是超過,直接寫入目標對象out流中,清空緩衝區
			if (len >= buf.length) {
				flushBuffer();
				out.write(b, off, len);
				return;
			}
			//若是長度大於緩衝區剩餘空間,將緩衝區清空,寫入數據
			if (len > buf.length - count) {
				flushBuffer();
			}
			System.arraycopy(b, off, buf, count, len);
			count += len;
		}
		
		/**
		*刷新緩衝區
		**/
		public synchronized void flush() throws IOException {
			flushBuffer();
			out.flush();
		}
	}

從上面的源碼中能夠發現 觸發緩衝區刷新的時機是當寫入數據大小大於緩衝區的可用大小;結合上面的業務代碼能夠發現問題: 假如最後一次(或者幾回)寫入緩衝區的數據小於緩衝區實際的大小,不足以出發清空緩衝區將數據寫入實際目標流的out對象中,並且沒有手動觸發刷新,就會形成數據丟失的問題。this

爲了解決該問題,將核心操做放到try-catche-finally中,在finally中手動關閉BufferedInputStream和BufferedOutputStream流(BufferedOutputStream並無close方法,調用父類FilterOutputStream的close方法),在關閉前會強制刷新緩衝區的數據到out寫對象流中。該問題獲得解決。指針

相關文章
相關標籤/搜索