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

先来一道开胃菜

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

         InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String s = null;
try {
System.out.println("开始输入 。。。");
s = br.readLine();
while (s != null) {
if (s.equalsIgnoreCase("yes")) {
break;
}
System.out.println(s.toUpperCase());
System.out.println("是否停止输入(yes/no)");
s = br.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}finally{
if(br !=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(isr != null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

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

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

稍微深入一点。我用Inputstream 去读取字符串并转化为想要的编码格式

 public static String getStrFormInputStream(String str,
String encoding) throws IOException {
InputStream inputStream = new ByteArrayInputStream(str.getBytes(encoding));
BufferedReader bf = null;
InputStreamReader inputStreamReader = null;
try {
inputStreamReader = new InputStreamReader(inputStream);
bf = new BufferedReader(inputStreamReader);
StringBuffer sb = new StringBuffer();
String line = "";
while ((line = bf.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} finally {
if (bf != null) {
bf.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (inputStream != null) {
inputStream.close();
}
} }

   这就偏实际一点,当你拿到一个字符串的时候,读取的时候,有一个细节:最好加上编码格式

   解释一下:实际上读取的地方 只有这一点 line = bf.readLine() ,那么之前的是做什么呢,  我其实是在组装我想要的铲子。这也是 开发中比较常用的“包装器模式”

我想把字符串转为贴合实际的ByteArrayInputStream, 再转化为更常用的Reader(InputStreamReader)  再包装上buffer(BufferedReader)。

当我选择输出的时候:

当我查看我以前的开发记录时,发现实际业务中 绝大多数输出都是输出到文件的。想找一个简单的输出示例,并不容易

 public void sendMessage(String str,
String host,
int port) throws UnsupportedEncodingException, IOException {
String sendEncoding = "GBK";
Writer writer = null;
Socket client = null;
try {
// 与服务端建立连接
client = new Socket(host, port);
// 建立连接后就可以往服务端写数据了
writer = new OutputStreamWriter(client.getOutputStream(), sendEncoding);
System.out.println(str);
writer.write(str);
writer.flush();// 写完后要记得flush
} finally {
if (writer != null) {
try {
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (client != null) {
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} }

  输出的地方其实很简单: writer.write(str); 其他的地方 建立服务器连接,将str写到短信中和此处关系不大,需要注意 :无论是输入输出,用完了一定关闭流,这是底线

我有一文件

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

 文件的基本操作 

String filePath = "D:\\";
File file = new File(filePath);
long length = 0;
if (file.exists()){
length = file.length();
}else{
file.mkdirs();
}

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

读写图片

  public void createImage(BufferedImage image,
String fileLocation) throws IOException {
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
fos = new FileOutputStream(fileLocation);
bos = new BufferedOutputStream(fos);
// JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos);
// encoder.encode(image);
ImageIO.write(image, "JPEG", bos);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
fos.close();
}
if (bos != null) {
bos.close();
}
}
}
     boolean isJob = etlTrans.getFromKjb();
byte[] xml = etlTrans.getXml();
ByteArrayInputStream bais = new ByteArrayInputStream(xml);
TransMeta transMeta = null;
JobMeta jobMeta = null;
int imageWidth = 1400;// 图片的宽度
int imageHeight = 900;// 图片的高度
int wordWidth = 6;
BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.getGraphics();
// 字体不支持时,中文就显示口口口了
graphics.setFont(new java.awt.Font("宋体", java.awt.Font.BOLD, 20));
// Font oldFont = graphics.getFont();
graphics.setColor(Color.WHITE);
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();
}

xml文件读写,实际上也是一样的,只不过调用的是xml提供的读写工具类

分文件写数据

 private void writeLog(FileOutputStream writer,
List<ExceptionLog> cellExceptionLogs) throws IOException {
StringBuilder builder = new StringBuilder();
int len = cellExceptionLogs.size();
for (int i = 0; i < len; i++) {
ExceptionLog exceptionLog = cellExceptionLogs.get(i);
processSystemLogData(exceptionLog, builder);
if ((i + 1) % 500 == 0) {
writer.write(builder.toString().getBytes("UTF-8"));
writer.flush();
builder = null;
builder = new StringBuilder();
}
}
if (len % 500 != 0) {
writer.write(builder.toString().getBytes("UTF-8"));
writer.flush();
}
}
private void processSystemLogData(ICellExceptionLog exception,
StringBuilder builder) {
builder.append(exception.getId() + Constant.REPORTUNITSEPARATOR);
builder.append(exception.getCode() + Constant.REPORTUNITSEPARATOR);
builder.append(exception.getName() + Constant.REPORTUNITSEPARATOR);
builder.append(exception.getDescription()+ Constant.REPORTUNITSEPARATOR);
builder.append("\r\n");
}

综合应用:编写一个页面,提供给用户下载数据。用户选择下载位置

本质上是一样,只不过输出的流特殊一点(参考于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项目的断点续传的服务器操作贴出(虽然我已经去了大多数和断点续传无关的业务逻辑,但是代码还是稍显复杂)

         public static final int buf_size = 8192;
ServletOutputStream out = response.getOutputStream();
RandomAccessFile raf = null;
File file = null;
try {
file = new File(filefullname);//这是我们写文件的数据来源
raf = new RandomAccessFile(file, "rw");
long pos = 0;
long end = 0;//这写开始写的位置,结束的位置可以从请求报文得到
long fLength = file.length();
//如果有断点续传的信息
if (queryStringMap.get("Range") != null) {
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);//设置返回信息为206
String range = ((String) queryStringMap.get("Range")).replaceAll("bytes=", "");
pos = Long.valueOf(range.split("-")[0]);
end = Long.valueOf(range.split("-")[1]);
} else {
end = fLength - 1;
}
//是我这次请求需要写多少长度的文件
long length = end - pos + 1;
//下面是返回报文信息
response.setHeader("Content-Length", String.valueOf(length));
response.setContentType("application/x-msdownload");
response.addHeader("content-disposition", "attachment;filename=\"" + filename + "\"");
//下边是调用RandomAccess方法进行断点续传
byte[] buf = new byte[buf_size];
int n = 0;
int i = 0;//用来for循环中统计写的次数
raf.seek(pos);// 定位文件指针到 pos 位置
// buf_size 是定义的大文件写的快,如果你的文件还没一块大,直接写就好
int p = (int) (length / buf_size);//我要写多少块(byte[])
int b_size = (int) (length % buf_size);
if (b_size > 0)
p++;
while (i < p) {
i++;
if (i == p && b_size > 0) {
buf = new byte[b_size];
n = raf.read(buf, 0, b_size);
} else {
n = raf.read(buf, 0, buf_size);
}
out.write(buf);
pos += n;
raf.seek(pos);
}
}catch(Exception e){
e.printStackTrace();
}finally{
// ....
}
//...

服务器操作完成之后返回的相应信息: 可以清楚的看到比普通的返回信息多了一个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类

 

给你一数据库

 protected Map<String, Object> getRecord(ResultSet rset,
ResultSetMetaData metaData,
int colnum) throws SQLException {
Map<String, Object> resultMap = new HashMap<String, Object>();
for (int columnCount = 1; columnCount <= colnum; columnCount++) {
String aliasName = metaData.getColumnLabel(columnCount);
Object columnValue = null;
String columnName = aliasName != null ? aliasName : metaData.getColumnName(columnCount);
int columnType = metaData.getColumnType(columnCount);
//...
if (columnType == Types.BLOB || columnType == Types.LONGVARCHAR
|| columnType == Types.LONGVARBINARY) {
if (rset.getBlob(columnName) != null) { InputStream res = rset.getBlob(columnName).getBinaryStream();
int BUFFER_SIZE = 4096;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
try {
byte[] data = new byte[4096];
int count = -1;
while ((count = res.read(data, 0, BUFFER_SIZE)) != -1)
outStream.write(data, 0, count); data = null;
columnValue = new String(outStream.toByteArray()); } catch (Exception e) {
throw new SQLException("GenericDaoImpl.jdbc.error");
} finally {
try {
if (outStream != null)
outStream.close();
if (res != null)
res.close();
} catch (IOException e) {
throw new SQLException("GenericDaoImpl.jdbc.error");
}
}
}
} else if (columnType == Types.CLOB) {
Clob clob = rset.getClob(columnName);
if (clob != null) {
columnValue = clob.getSubString((long) 1, (int) clob.length());
}
} else {
columnValue = rset.getObject(columnName);
}
resultMap.put(columnName.toUpperCase(), columnValue);
}
return resultMap;
}

数据库中读取记录代码

一般情况下,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

  

  

  

Java IO在实际开发中的应用的更多相关文章

  1. Java 反射在实际开发中的应用

    运行时类型识别(RTTI, Run-Time Type Information)是Java中非常有用的机制,在java中,有两种RTTI的方式,一种是传统的,即假设在编译时已经知道了所有的类型:还有一 ...

  2. [转]Java 反射在实际开发中的应用

    一:Java类加载和初始化 1.1 类加载器(类加载的工具) 1.2 Java使用一个类所需的准备工作 二:Java中RTTI 2.1 :为什么要用到运行时类型信息(就是RTTI) 2.2  :RTT ...

  3. Android学习探索之Java 8 在Android 开发中的应用

    前言: Java 8推出已经将近2年多了,引入很多革命性变化,加入了函数式编程的特征,使基于行为的编程成为可能,同时减化了各种设计模式的实现方式,是Java有史以来最重要的更新.但是Android上, ...

  4. Java 数据类型在实际开发中应用

    在前边的博文中,我已经介绍了Java核心的容器IO等,现在我来说一下java中的数据类型.在java中,一切东西皆为对象(这句话意思是java中绝大数情况都用对象),极少数不是对象的,也存在与之对应的 ...

  5. Java 泛型在实际开发中的应用

    java泛型是对Java语言的类型系统的一种扩展,泛型的本质就是将所操作的数据类型参数化.下面我会由浅入深地介绍Java的泛型. 一:泛型出现的背景 在java代码里,你会经常发现类似下边的代码: p ...

  6. Java和.NET在开发中的不同盘点

    我是用VS2008和VS2010开发.NET程序,通过MyEclipse8.5开发JAVA程序,下面从IDE.语言.插件的不同点来做下简单的说明.但由于经验知识还有限,本篇文章只能从比较表面的以及自己 ...

  7. Java数据类型在实际开发中的应用一

    在前边的博文中,我已经介绍了Java核心的容器IO等,现在我来说一下java中的数据类型.在java中,一切东西皆为对象(这句话意思是java中绝大数情况都用对象),极少数不是对象的,也存在与之对应的 ...

  8. Java数据类型在实际开发中的应用二枚举类型

    在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的.在JDK1.5之前,人们用接口来描述这一种数据类型. 1. ...

  9. Java 数据类型在实际开发中应用二枚举

    在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的.在JDK1.5之前,人们用接口来描述这一种数据类型. 1. ...

随机推荐

  1. 全局精确流量调度新思路-HttpDNS服务详解

    但凡使用域名来给用户提供服务的互联网企业,都或多或少地无法避免在有中国特色的互联网环境中遭遇到各种域名被缓存.用户跨网访问缓慢等问题.那么对于腾讯这样的域名数量在10万级别的互联网公司来讲,域名解析异 ...

  2. Win10 + Python + GPU版MXNet + VS2015 + RTools + R配置

    最近入手一台GTX 1070的笔记本,手痒想在win10上试下GPU跑模型,所以就有了接下来的安装GPU版mxnet的坎坷历程,经过多重试验终于搞定了python和R安装mxnet,现将主要点记录如下 ...

  3. 浅谈JAVA验证码~

    这两天在帮同学做个项目,项目中需要做个验证码,说实话那么多年竟然没注意过这东西,原理很简单,贴出来给大家做个参考. 1.简单介绍 一般稍微有些经验的程序员都不会再自己写原生验证码生成了,因为各种强大的 ...

  4. JAVA POI 应用系列(2)--读取Excel

    添加maven依赖 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi< ...

  5. Python教程(0)——介绍

    What's Python? Python是一种编程语言,由荷兰人Guido van Rossum于1989年,为打发无聊的圣诞节而开发的. Python的优点是语法简洁,代码量比C/C++.Java ...

  6. Ionic3新特性--页面懒加载2加载其他组件

    在第一节中,我们介绍了页面的懒加载方式,并进行了初步的分析,这里,我们将进一步介绍如何配合页面懒加载进行其他组件Component.Pipe.Directive等的模块化,和加载使用. 首先说明一点, ...

  7. Xamarin开发笔记—WebView双项事件调用

    1.Xamarin调用WebView: 原理:Xamarin.Forms WebView内置方法xx.Eval(..)可以调用到页面里面的js函数. WebView展示的代码如下: var htmlS ...

  8. HTML5 客户端存储数据的两种方式

    HTML5 提供了两种在客户端存储数据的新方法: localStorage - 没有时间限制的数据存储 sessionStorage - 针对一个 session 的数据存储 之前,这些都是由 coo ...

  9. Bash环境配置文件

    一.环境配置文件读取优先级 其中~/.bash_profile,~/.bash_login,~/.profile三个文件只有一个有效,查找优先级从左至右降低.bash会一直检查是否有~/.bashrc ...

  10. 电脑上的windows键突然失灵了,肿么办

    windows经常会用到,或许平时感觉不出异常来,偶尔用一次的时候,去发现失灵了,肿么办? 如果只是单纯的弹出开始菜单来,可以按Ctrl+Esc,功能是一样的. 这种情况其实是windows被禁用了, ...