IO是java繞不過去的檻,在開發中io無處不在, 正如同 世界上本沒有路,java io寫多了,也就知道了大致是什麼意思,在讀完thinking in java 感受就更清晰了,結合具體的業務場景,整理一下 ,什麼是IO。爲何JAVA要這麼設計IO。html
1 InputStreamReader isr = new InputStreamReader(System.in); 2 BufferedReader br = new BufferedReader(isr); 3 String s = null; 4 try { 5 System.out.println("開始輸入 。。。"); 6 s = br.readLine(); 7 while (s != null) { 8 if (s.equalsIgnoreCase("yes")) { 9 break; 10 } 11 System.out.println(s.toUpperCase()); 12 System.out.println("是否中止輸入(yes/no)"); 13 s = br.readLine(); 14 } 15 } catch (IOException e) { 16 e.printStackTrace(); 17 }finally{ 18 if(br !=null){ 19 try { 20 br.close(); 21 } catch (IOException e) { 22 e.printStackTrace(); 23 } 24 } 25 if(isr != null){ 26 try { 27 isr.close(); 28 } catch (IOException e) { 29 e.printStackTrace(); 30 } 31 } 32 } 33
解釋一下:我從控制檯讀取一行字符,而後打印一下。這就是一個簡單的流了。java
整理一下: 就是我先 獲得一個用於讀取 控制檯輸入的流,而後 我·打印我獲得的東西,這裏有個細節就是 流必定得關閉,這是底線,關閉的順序:先聲明的後關閉web
1 public static String getStrFormInputStream(String str, 2 String encoding) throws IOException { 3 InputStream inputStream = new ByteArrayInputStream(str.getBytes(encoding)); 4 BufferedReader bf = null; 5 InputStreamReader inputStreamReader = null; 6 try { 7 inputStreamReader = new InputStreamReader(inputStream); 8 bf = new BufferedReader(inputStreamReader); 9 StringBuffer sb = new StringBuffer(); 10 String line = ""; 11 while ((line = bf.readLine()) != null) { 12 sb.append(line); 13 } 14 return sb.toString(); 15 } finally { 16 if (bf != null) { 17 bf.close(); 18 } 19 if (inputStreamReader != null) { 20 inputStreamReader.close(); 21 } 22 if (inputStream != null) { 23 inputStream.close(); 24 } 25 } 26 27 }
這就偏實際一點,當你拿到一個字符串的時候,讀取的時候,有一個細節:最好加上編碼格式數據庫
解釋一下:實際上讀取的地方 只有這一點 line = bf.readLine() ,那麼以前的是作什麼呢, 我實際上是在組裝我想要的鏟子。這也是 開發中比較經常使用的「包裝器模式」數組
我想把字符串轉爲貼合實際的ByteArrayInputStream, 再轉化爲更經常使用的Reader(InputStreamReader) 再包裝上buffer(BufferedReader)。 瀏覽器
當我查看我之前的開發記錄時,發現實際業務中 絕大多數輸出都是輸出到文件的。想找一個簡單的輸出示例,並不容易服務器
1 public void sendMessage(String str, 2 String host, 3 int port) throws UnsupportedEncodingException, IOException { 4 String sendEncoding = "GBK"; 5 Writer writer = null; 6 Socket client = null; 7 try { 8 // 與服務端創建鏈接 9 client = new Socket(host, port); 10 // 創建鏈接後就能夠往服務端寫數據了 11 writer = new OutputStreamWriter(client.getOutputStream(), sendEncoding); 12 System.out.println(str); 13 writer.write(str); 14 writer.flush();// 寫完後要記得flush 15 } finally { 16 if (writer != null) { 17 try { 18 writer.close(); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 } 23 if (client != null) { 24 try { 25 client.close(); 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } 29 } 30 } 31 32 }
輸出的地方其實很簡單: writer.write(str); 其餘的地方 創建服務器鏈接,將str寫到短信中和此處關係不大,須要注意 :不管是輸入輸出,用完了必定關閉流,這是底線 多線程
文件讀寫纔是一個真正的業務場景中佔比絕大多數的。app
String filePath = "D:\\"; File file = new File(filePath); long length = 0; if (file.exists()){ length = file.length(); }else{ file.mkdirs(); }
在java中 File 就是 File , 它既能夠是具體的文件,也能夠是一個文件目錄cors
當你讀寫文件的時候,看到直接讀取字節實際上應用更廣一點,固然用讀取字符也是佔很大比重,因此,這個問題就丟給各位讀者,究竟是想用哪一個。反正都行。
public byte[] getBytesByFileName(String filePath) { byte[] buffer = null; try { File file = new File(filePath); FileInputStream fis = new FileInputStream(file); ByteArrayOutputStream bos = new ByteArrayOutputStream(1000); byte[] b = new byte[1000]; int n; while ((n = fis.read(b)) != -1) { bos.write(b, 0, n); } bos.close(); buffer = bos.toByteArray(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return buffer; }
public static void moveFile(String sourcePath, String targetPath) throws Exception { File sourceRoot = new File(sourcePath); if (!sourceRoot.exists()) { throw new Exception("要移動的文件不存在"); } if (sourceRoot.isFile()) { boolean success = true; File targetFile = new File(targetPath); if (!targetFile.getParentFile().exists()) { if (!targetFile.getParentFile().mkdirs()) { success = false; } } if (!targetFile.exists()) { if (!targetFile.createNewFile()) { success = false; } } if (!success) { throw new Exception("目標目錄建立失敗"); } BufferedInputStream bis = null; BufferedOutputStream bos = null; byte[] d = new byte[1024]; int length = -1; try { bis = new BufferedInputStream(new FileInputStream(sourceRoot)); bos = new BufferedOutputStream(new FileOutputStream(targetFile)); while ((length = bis.read(d)) != -1) { bos.write(d, 0, length); } bos.flush(); } catch (IOException e) { e.printStackTrace(); success = false; } finally { if (bos != null) { bos.close(); } if (bis != null) { bis.close(); } bos = null; bis = null; } if (success) { sourceRoot.deleteOnExit(); } } else { File[] files = sourceRoot.listFiles(); for (File file : files) { moveFile(file.getAbsolutePath(), targetPath + File.separator + file.getName()); } sourceRoot.deleteOnExit(); } }
移動文件:實際上就是從一個文件中讀取文件,而後寫到另外一個文件中,這算是一個很是詳細的例子。分析代碼:我想判斷源文件是否存在,我再去建立目標文件夾和目標文件,固然,你也能夠不用mkdir()
直接用 mkdirs()也行。
固然我寫文件時的數據(調用write()方法傳入的數據)不必定是來自文件也有可能來自一個list,一個byte[]數組。
public void writeFile(String str, File file) throws IOException { OutputStream out = null; try { out = new FileOutputStream(file, true); // 是否追加 byte[] b = str.getBytes(); out.write(b); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { if (out != null) { out.close(); } } }
public void exportByModel(List<Map<String, Object>> data, byte[] exportModel, String fileNameLocation) throws Exception { InputStream in = null; Reader reader = null; OutputStream out = null; Writer des = null; CharArrayWriter writer = null; try { // 讀取模板 in = new ByteArrayInputStream(exportModel); reader = new InputStreamReader(in); // 設置輸出位置 out = new FileOutputStream(fileNameLocation); String encoding = "GBK"; try { des = new OutputStreamWriter(out, encoding);// 不設置utf-8,中文不支持 } catch (Exception e) { des = new OutputStreamWriter(out, "GBK");// 編碼設置異常,直接按照GBK輸出 } // 執行 writer = VelocityHelper.getInstance().evaluateToWriter(data, reader); writer.writeTo(des); des.flush(); } catch (Exception e) { throw new Exception("寫入文件異常"); } finally { if (writer != null) writer.close(); if (des != null) des.close(); if (out != null) out.close(); if (reader != null) reader.close(); if (in != null) in.close(); } }
1 public void createImage(BufferedImage image, 2 String fileLocation) throws IOException { 3 FileOutputStream fos = null; 4 BufferedOutputStream bos = null; 5 try { 6 fos = new FileOutputStream(fileLocation); 7 bos = new BufferedOutputStream(fos); 8 // JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos); 9 // encoder.encode(image); 10 ImageIO.write(image, "JPEG", bos); 11 } catch (Exception e) { 12 e.printStackTrace(); 13 } finally { 14 if (fos != null) { 15 fos.close(); 16 } 17 if (bos != null) { 18 bos.close(); 19 } 20 } 21 }
1 boolean isJob = etlTrans.getFromKjb(); 2 byte[] xml = etlTrans.getXml(); 3 ByteArrayInputStream bais = new ByteArrayInputStream(xml); 4 TransMeta transMeta = null; 5 JobMeta jobMeta = null; 6 int imageWidth = 1400;// 圖片的寬度 7 int imageHeight = 900;// 圖片的高度 8 int wordWidth = 6; 9 BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB); 10 Graphics graphics = image.getGraphics(); 11 // 字體不支持時,中文就顯示口口口了 12 graphics.setFont(new java.awt.Font("宋體", java.awt.Font.BOLD, 20)); 13 // Font oldFont = graphics.getFont(); 14 graphics.setColor(Color.WHITE); 15 graphics.fillRect(0, 0, imageWidth, imageHeight);
public static void objectXmlEncoder(Object obj, String fileName) throws FileNotFoundException, IOException, Exception { // 建立輸出文件 File fo = new File(fileName); // 文件不存在,就建立該文件 if (!fo.exists()) { // 先建立文件的目錄 String path = fileName.substring(0, fileName.lastIndexOf('.')); File pFile = new File(path); pFile.mkdirs(); } // 建立文件輸出流 FileOutputStream fos = new FileOutputStream(fo); // 建立XML文件對象輸出類實例 XMLEncoder encoder = new XMLEncoder(fos); // 對象序列化輸出到XML文件 encoder.writeObject(obj); encoder.flush(); // 關閉序列化工具 encoder.close(); // 關閉輸出流 fos.close(); }
xml文件讀寫,實際上也是同樣的,只不過調用的是xml提供的讀寫工具類
1 private void writeLog(FileOutputStream writer, 2 List<ExceptionLog> cellExceptionLogs) throws IOException { 3 StringBuilder builder = new StringBuilder(); 4 int len = cellExceptionLogs.size(); 5 for (int i = 0; i < len; i++) { 6 ExceptionLog exceptionLog = cellExceptionLogs.get(i); 7 processSystemLogData(exceptionLog, builder); 8 if ((i + 1) % 500 == 0) { 9 writer.write(builder.toString().getBytes("UTF-8")); 10 writer.flush(); 11 builder = null; 12 builder = new StringBuilder(); 13 } 14 } 15 if (len % 500 != 0) { 16 writer.write(builder.toString().getBytes("UTF-8")); 17 writer.flush(); 18 } 19 } 20 private void processSystemLogData(ICellExceptionLog exception, 21 StringBuilder builder) { 22 builder.append(exception.getId() + Constant.REPORTUNITSEPARATOR); 23 builder.append(exception.getCode() + Constant.REPORTUNITSEPARATOR); 24 builder.append(exception.getName() + Constant.REPORTUNITSEPARATOR); 25 builder.append(exception.getDescription()+ Constant.REPORTUNITSEPARATOR); 26 builder.append("\r\n"); 27 }
本質上是同樣,只不過輸出的流特殊一點(參考於java web文件下載功能實現)
第一步:寫一個提供給用戶的按鈕
<a href="/day06/ServletDownload?filename=cors.zip">壓縮包</a>
第二步:編寫對應的servlet方法。一共分兩步:把文件讀入文件流,把文件流內的數據寫入到response中(response.getOutputStream().write(bytes, 0, bytes.length);)
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //得到請求文件名 String filename = request.getParameter("filename"); System.out.println(filename); //設置文件MIME類型 response.setContentType(getServletContext().getMimeType(filename)); //設置Content-Disposition response.setHeader("Content-Disposition", "attachment;filename="+filename); //讀取目標文件,經過response將目標文件寫到客戶端 //獲取目標文件的絕對路徑 String fullFileName = getServletContext().getRealPath("/download/" + filename); //System.out.println(fullFileName); //讀取文件 InputStream in = new FileInputStream(fullFileName); OutputStream out = response.getOutputStream(); //寫文件 int b; while((b=in.read())!= -1) { out.write(b); } · in.close(); out.close(); }
所謂斷點續傳 就是文件已經下載的地方開始繼續下載。因此在客戶端瀏覽器傳給 Web服務器的時候要多加一條信息--從哪裏開始。
這是瀏覽器想要斷點續傳的時候發給服務器的信息
// GET /down.zip HTTP/1.0 // User-Agent: NetFox // RANGE: bytes=2000070- // Accept: text/html, image/gif, image/jpeg, *; q=.2, *; q=.2 // 上邊的信息,是瀏覽器發給web用戶的斷點續傳的信息
服務器的操做: 這是一段簡單的模擬從必定位置些文件的代碼
//假設從 2000070 處開始保存文件,代碼以下: RandomAccess oSavedFile = new RandomAccessFile("down.zip","rw"); long nPos = 2000070; // 定位文件指針到 nPos 位置 oSavedFile.seek(nPos); byte[] b = new byte[1024]; int nRead; // 從輸入流中讀入字節流,而後寫到文件中 while((nRead=input.read(b,0,1024)) > 0) { oSavedFile.write(b,0,nRead);
實際上,sevlet斷點續傳確定不是這麼簡單,可是整體思路一致,我猶豫很久,最終仍是把真正用於web項目的斷點續傳的服務器操做貼出(雖然我已經去了大多數和斷點續傳無關的業務邏輯,可是代碼仍是稍顯複雜)
1 public static final int buf_size = 8192; 2 ServletOutputStream out = response.getOutputStream(); 3 RandomAccessFile raf = null; 4 File file = null; 5 try { 6 file = new File(filefullname);//這是咱們寫文件的數據來源 7 raf = new RandomAccessFile(file, "rw"); 8 long pos = 0; 9 long end = 0;//這寫開始寫的位置,結束的位置能夠從請求報文獲得 10 long fLength = file.length(); 11 //若是有斷點續傳的信息 12 if (queryStringMap.get("Range") != null) { 13 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);//設置返回信息爲206 14 String range = ((String) queryStringMap.get("Range")).replaceAll("bytes=", ""); 15 pos = Long.valueOf(range.split("-")[0]); 16 end = Long.valueOf(range.split("-")[1]); 17 } else { 18 end = fLength - 1; 19 } 20 //是我此次請求須要寫多少長度的文件 21 long length = end - pos + 1; 22 //下面是返回報文信息 23 response.setHeader("Content-Length", String.valueOf(length)); 24 response.setContentType("application/x-msdownload"); 25 response.addHeader("content-disposition", "attachment;filename=\"" + filename + "\""); 26 //下邊是調用RandomAccess方法進行斷點續傳 27 byte[] buf = new byte[buf_size]; 28 int n = 0; 29 int i = 0;//用來for循環中統計寫的次數 30 raf.seek(pos);// 定位文件指針到 pos 位置 31 // buf_size 是定義的大文件寫的快,若是你的文件還沒一塊大,直接寫就好 32 int p = (int) (length / buf_size);//我要寫多少塊(byte[]) 33 int b_size = (int) (length % buf_size); 34 if (b_size > 0) 35 p++; 36 while (i < p) { 37 i++; 38 if (i == p && b_size > 0) { 39 buf = new byte[b_size]; 40 n = raf.read(buf, 0, b_size); 41 } else { 42 n = raf.read(buf, 0, buf_size); 43 } 44 out.write(buf); 45 pos += n; 46 raf.seek(pos); 47 } 48 }catch(Exception e){ 49 e.printStackTrace(); 50 }finally{ 51 // .... 52 } 53 //... 54
服務器操做完成以後返回的相應信息: 能夠清楚的看到比普通的返回信息多了一個content-range
//Content-Length=106786028 //Content-Range=bytes 2000070-106786027/106786028 //Date=Mon, 30 Apr 2001 12:55:20 GMT //ETag=W/"02ca57e173c11:95b" //Content-Type=application/octet-stream //Server=Microsoft-IIS/5.0 //Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT
固然實際項目都是多線程的,能夠參考Java--使用多線程下載,斷點續傳技術原理(RandomAccessFile)
RandomAccessFile的經常使用方法介紹 能夠參考java的RandomAccessFile類
1 protected Map<String, Object> getRecord(ResultSet rset, 2 ResultSetMetaData metaData, 3 int colnum) throws SQLException { 4 Map<String, Object> resultMap = new HashMap<String, Object>(); 5 for (int columnCount = 1; columnCount <= colnum; columnCount++) { 6 String aliasName = metaData.getColumnLabel(columnCount); 7 Object columnValue = null; 8 String columnName = aliasName != null ? aliasName : metaData.getColumnName(columnCount); 9 int columnType = metaData.getColumnType(columnCount); 10 //... 11 if (columnType == Types.BLOB || columnType == Types.LONGVARCHAR 12 || columnType == Types.LONGVARBINARY) { 13 if (rset.getBlob(columnName) != null) { 14 15 InputStream res = rset.getBlob(columnName).getBinaryStream(); 16 int BUFFER_SIZE = 4096; 17 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 18 try { 19 byte[] data = new byte[4096]; 20 int count = -1; 21 while ((count = res.read(data, 0, BUFFER_SIZE)) != -1) 22 outStream.write(data, 0, count); 23 24 data = null; 25 columnValue = new String(outStream.toByteArray()); 26 27 } catch (Exception e) { 28 throw new SQLException("GenericDaoImpl.jdbc.error"); 29 } finally { 30 try { 31 if (outStream != null) 32 outStream.close(); 33 if (res != null) 34 res.close(); 35 } catch (IOException e) { 36 throw new SQLException("GenericDaoImpl.jdbc.error"); 37 } 38 } 39 } 40 } else if (columnType == Types.CLOB) { 41 Clob clob = rset.getClob(columnName); 42 if (clob != null) { 43 columnValue = clob.getSubString((long) 1, (int) clob.length()); 44 } 45 } else { 46 columnValue = rset.getObject(columnName); 47 } 48 resultMap.put(columnName.toUpperCase(), columnValue); 49 } 50 return resultMap; 51 }
通常狀況下,web項目採用一些orm框架足以支撐,數據庫字段的讀寫,可是,有時候爲了效率或者是特有的業務要求。會本身編寫dao層支持。
而在讀取數據庫記錄,寫入Map的時候,如何讀取一些特殊字段,好比Blob, 上述代碼就是描述如何讀取blob字段
文件的讀寫 ,在java中是很是經常使用。
而設計者設計讀寫控制的時候,也整理的頗爲詳細,可能的結果就是,給調用者帶來很大的困惑,這麼多類,咋用。
其實徹底不用擔憂:java寫文件只有三步,百變不離其宗
1:我找到三樣東西: 鏟子(IO工具類); 沙子(我要讀取的數據); 簍子(我要放的東西)
2:用鏟子把沙子放到簍子裏
3:我把鏟子還給人家
至於我用 何種鏟子,我要鏟的是沙子 仍是麪粉,我要放到簍子仍是框子裏。先別管,也別上來就看那個javaIIO的類示意圖
java中有讀寫字符的(reader/writer) 讀寫字節的(inputstream,outputstream)。自由選擇
java按照功能 還會有 filereader xmlreder imgio buffereader 等
字符串,byte[] , List<T>,數據庫查詢的結果集
能夠放到Map String 圖片 文件 xml