最近在做一個需求時,遇到流的問題,在這總結下:
需求是,我要調別人的接口,對方給我返回一個pdf的電子發票流,我接收后進行保存到本地
首先貼一張猿友的圖,
IO流分兩種;字符流&字節流
百度===>字節流和字符流
什么是流
流是個抽象的概念,是對輸入輸出設備的抽象,輸入流可以看作一個輸入通道,輸出流可以看作一個輸出通道。
輸入流是相對程序而言的,外部傳入數據給程序需要借助輸入流。
輸出流是相對程序而言的,程序把數據傳輸到外部需要借助輸出流。
字節流與字符流
字節流是由字節組成的,字符流是由字符組成的字節與字符
位:數據存儲的最小單位。每個二進制數字0或者1就是1個位;
字節:8個位構成一個字節;即:1 byte (字節)= 8 bit(位);
字符:a、A、中、+、*、の......均表示一個字符;一般utf-8 編碼下,一個漢字字符占用3個字節;一般gbk 編碼下,一個漢字字符占用2個字節;繼續百度
字節數據是二進制形式的,是我們計算機存儲的一種形式;
字符就是我們在各種客戶端看到的各種文字,是內存中的一種狀態;
字節與字符之間差了一個編碼;
回到最初的需求,要在別人接口中獲取一個pdf流,那么獲取到的是什么?
我們知道網絡傳輸的都是二級制數據,字符流又不是二級制,那么不論對方是怎么處理的文件,他最終傳給我的一定是字節流。
所以不管接到后怎么處理,反正直接節流就對了,
// 獲取一個瀏覽器 DefaultHttpClient httpclient = new DefaultHttpClient(); // 獲取get請求 HttpGet httpget = new HttpGet(url); // 請求對方服務器 HttpResponse response = httpclient.execute(httpget); // 獲取返回數據 InputStream inputStream = response.getEntity().getContent();
事實證明了上面的結論,返回的確實是字節流;
然后怎么處理呢?
那就直接把字節流保存吧,反正保存后也是字節,
// new 一個本地文件 File file = new File("D:\\11111.pdf"); // 創建相對於程序的輸出流 OutputStream outputStream = null; try { outputStream = new FileOutputStream(file); // 讀取數據 byte[] b = new byte[1024];// b - 數據 int off = 0;// off - 數據中的起始偏移量。 int len = -1;// len - 要寫入的字節數。 while ((len = inputStream.read(b)) != -1) {// 從輸入流讀取一些字節數,並將它們存儲到緩沖區b。實際讀取的字節數作為整數返回,如果沒有字節可用,因為流在文件末尾,則返回值-1;否則,讀取至少一個字節並存儲到b。 outputStream.write(b, off, len);// 數組b中的一些字節按順序寫入輸出流; 元素off是寫入的第一個字節,len是此操作寫入的最后一個字節。 } outputStream.flush();// 刷新此輸出流並強制任何緩沖的輸出字節被寫出。 } catch (IOException e) { System.out.println("異常"); e.printStackTrace(); } finally { inputStream.close();//關閉此輸入流並釋放與此流相關聯的任何系統資源。 outputStream.close();//關閉此輸出流並釋放與此流相關聯的任何系統資源。 }
然后嘗試他們的子類們,例:BufferedInputStream 與 BufferedOutputStream
// 獲取一個瀏覽器 DefaultHttpClient httpclient = new DefaultHttpClient(); // 獲取get請求 HttpGet httpget = new HttpGet(url); // 請求對方服務器 HttpResponse response = httpclient.execute(httpget); // 獲取返回數據 BufferedInputStream inputStream = (BufferedInputStream)response.getEntity().getContent(); // new 一個本地文件 File file = new File("D:\\11111.pdf"); // 創建相對於程序的輸出流 BufferedOutputStream outputStream = null; try { FileOutputStream fileOutputStream = new FileOutputStream(file); outputStream = new BufferedOutputStream(fileOutputStream); // 讀取數據 byte[] b = new byte[1024];// b - 數據 int off = 0;// off - 數據中的起始偏移量。 int len = -1;// len - 要寫入的字節數。 while ((len = inputStream.read(b)) != -1) {// 從輸入流讀取一些字節數,並將它們存儲到緩沖區b。實際讀取的字節數作為整數返回,如果沒有字節可用,因為流在文件末尾,則返回值-1;否則,讀取至少一個字節並存儲到b。 outputStream.write(b, off, len);// 數組b中的一些字節按順序寫入輸出流; 元素off是寫入的第一個字節,len是此操作寫入的最后一個字節。 } outputStream.flush();// 刷新此輸出流並強制任何緩沖的輸出字節被寫出。 } catch (IOException e) { System.out.println("異常"); e.printStackTrace(); } finally { inputStream.close();//關閉此輸入流並釋放與此流相關聯的任何系統資源。 outputStream.close();//關閉此輸出流並釋放與此流相關聯的任何系統資源。 }
他們的子類在使用中並沒有太大的區別,網上扒了下他們各自的特點,至於具體選擇使用哪個類,可以參考這篇文章:https://www.cnblogs.com/penghuster/p/4869153.html
*********************************************
*******************字符流*********************
*********************************************
然后,如果接口返回的是字符串應該怎么處理呢,於是我換了一個get地址,返回的是一個字符串。
對於這樣的返回,我們一般情況下肯定是不需要保存的的,肯定是在內存中轉為字符串進行編輯展示什么的,但是為了理解流之間的關系,保存一下試試,
// 獲取一個瀏覽器 DefaultHttpClient httpclient = new DefaultHttpClient(); // 獲取get請求 HttpGet httpget = new HttpGet(url); // 請求對方服務器 HttpResponse response = httpclient.execute(httpget); // 獲取返回數據 InputStream inputStream = response.getEntity().getContent(); InputStreamReader inputStreamReader = null; OutputStreamWriter outputStreamWriter = null; try { // 將返回的字節流轉為字符流 inputStreamReader = new InputStreamReader(inputStream,"utf-8"); // 再將轉出的字符流再轉回字節流保存到磁盤 File file = new File("D:\\11.txt");// 二級制字節文件 FileOutputStream fileOutputStream = new FileOutputStream(file);// 用於程序輸出的字節流 outputStreamWriter = new OutputStreamWriter(fileOutputStream,"utf-8");// 將字符流轉為字節流 // 讀取數據 char[] chars = new char[1024]; int off =0; int len = -1; while ((len=inputStreamReader.read(chars))!=-1){ System.out.println(len); outputStreamWriter.write(chars,off,len); } } catch (IOException e) { System.out.println("異常"); e.printStackTrace(); } finally { // 關閉資源 outputStreamWriter.flush(); outputStreamWriter.close(); inputStreamReader.close(); }
事實證明是可以的,接口返回字節流,然后將字節流轉為我們認識的字符,再將我們認識的字符轉為二進制字節進行保存,基本可以明白流之間的關系了。
那么正常情況下怎么轉為字符串打印呢?
// 獲取一個瀏覽器 DefaultHttpClient httpclient = new DefaultHttpClient(); // 獲取get請求 HttpGet httpget = new HttpGet(url); // 請求對方服務器 HttpResponse response = httpclient.execute(httpget); // 獲取返回數據 InputStream inputStream = response.getEntity().getContent(); InputStreamReader inputStreamReader = null; BufferedReader bufferedReader = null; try { // 將字節流轉為字符流 inputStreamReader = new InputStreamReader(inputStream,"utf-8"); // BufferedReader類是從字符輸入流中讀取文本並緩沖字符,以便有效地讀取字符,數組和行 bufferedReader = new BufferedReader(inputStreamReader); // 創建保存字符串的StringBuffer StringBuffer str = new StringBuffer(); String s = null; while ((s=bufferedReader.readLine())!=null){ str.append(s); } // 打印 System.out.println(str.toString()); } catch (IOException e) { System.out.println("異常"); e.printStackTrace(); } finally { inputStreamReader.close(); bufferedReader.close(); }
這里有一點點不用,不再使用輸出流,因為相對於程序沒有寫磁盤嘛,所以用不着輸出流。
使用了BufferedReader ,BufferedReader類是從字符輸入流中讀取文本並緩沖字符,以便有效地讀取字符,數組和行,具體解釋參考:https://blog.csdn.net/ai_bao_zi/article/details/81134801
還用了StringBuffer ,補充一個String,StringBuffer,StringBuilder三者的使用方法和區別:https://blog.csdn.net/qq_37856300/article/details/84340288