• Java IO在实际开发中的应用


       IO是java绕不过去的槛,在开发中io无处不在, 正如同 世界上本没有路,java io写多了,也就知道了大体是什么意思,在读完thinking in java 感觉就更清晰了,结合具体的业务场景,整理一下 ,什么是IO。为什么JAVA要这么设计IO。

    先来一道开胃菜

     我想要读取控制台输入的字符

     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

      解释一下:我从控制台读取一行字符,然后打印一下。这就是一个简单的流了。

      整理一下: 就是我先 得到一个用于读取 控制台输入的流,然后 我·打印我得到的东西,这里有个细节就是 流一定得关闭,这是底线,关闭的顺序:先声明的后关闭

      稍微深入一点。我用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写到短信中和此处关系不大,需要注意 :无论是输入输出,用完了一定关闭流,这是底线 

     我有一文件

      文件读写才是一个真正的业务场景中占比绝大多数的。

     文件的基本操作 

    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 , 它既可以是具体的文件,也可以是一个文件目录

    读写文件总体步骤

    当你读写文件的时候,看到直接读取字节实际上应用更广一点,当然用读取字符也是占很大比重,所以,这个问题就丢给各位读者,到底是想用哪个。反正都行。

    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("
    ");
    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

       

      

      

  • 相关阅读:
    二叉排序树(B-Tree)-c实现
    队列(Queue)-c实现
    栈(stack)--c实现(使用双链表)
    链表(list)--c实现
    c 和 指针读书笔记(1)
    c traps and pitfalls reading notes(2)
    js 控制
    正则表达式
    Android 笔记
    Android 布局方式学习
  • 原文地址:https://www.cnblogs.com/ldh-better/p/7158658.html
Copyright © 2020-2023  润新知