Java IO在實際開發中的應用

   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     
View Code

  解釋一下:我從控制檯讀取一行字符,而後打印一下。這就是一個簡單的流了。java

  整理一下: 就是我先 獲得一個用於讀取 控制檯輸入的流,而後 我·打印我獲得的東西,這裏有個細節就是 流必定得關閉,這是底線,關閉的順序:先聲明的後關閉web

  稍微深刻一點。我用Inputstream 去讀取字符串並轉化爲想要的編碼格式

 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     }
View Code

   這就偏實際一點,當你拿到一個字符串的時候,讀取的時候,有一個細節:最好加上編碼格式數據庫

   解釋一下:實際上讀取的地方 只有這一點 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     }
View Code

  輸出的地方其實很簡單: 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();
        }
View Code

在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;
    }
View Code
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();
        }
    }
View Code

  移動文件:實際上就是從一個文件中讀取文件,而後寫到另外一個文件中,這算是一個很是詳細的例子。分析代碼:我想判斷源文件是否存在,我再去建立目標文件夾和目標文件,固然,你也能夠不用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();
            }
        }
    }
View Code
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();
        }
    }
View Code

讀寫圖片

 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      }
View Code
 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);

讀寫xml文件

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();
    }
View Code

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();  
    }  
View Code

 

斷點續傳 

   所謂斷點續傳 就是文件已經下載的地方開始繼續下載。因此在客戶端瀏覽器傳給 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     
View Code

 

服務器操做完成以後返回的相應信息: 能夠清楚的看到比普通的返回信息多了一個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字段

 IO歸納:

 文件的讀寫 ,在java中是很是經常使用。

 而設計者設計讀寫控制的時候,也整理的頗爲詳細,可能的結果就是,給調用者帶來很大的困惑,這麼多類,咋用。

 其實徹底不用擔憂:java寫文件只有三步,百變不離其宗

    1:我找到三樣東西:   鏟子(IO工具類); 沙子(我要讀取的數據); 簍子(我要放的東西)

    2:用鏟子把沙子放到簍子裏

    3:我把鏟子還給人家

  至於我用 何種鏟子,我要鏟的是沙子 仍是麪粉,我要放到簍子仍是框子裏。先別管,也別上來就看那個javaIIO的類示意圖   

按鏟子區分:通常單一的鏟子不適用,須要組合多種功能

  java中有讀寫字符的(reader/writer) 讀寫字節的(inputstream,outputstream)。自由選擇

  java按照功能 還會有 filereader xmlreder imgio   buffereader 等

按照沙子來看:

  字符串,byte[] , List<T>,數據庫查詢的結果集

按照簍子來看

  能夠放到Map String 圖片 文件 xml

相關文章
相關標籤/搜索