最近在做一个需求时,遇到流的问题,在这总结下:
需求是,我要调别人的接口,对方给我返回一个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();//关闭此输出流并释放与此流相关联的任何系统资源。
}他们的子类在使用中并没有太大的区别,网上扒了下他们各自的特点,至于具体选择使用哪个类,可以参考这篇文章:
*********************************************
*******************字符流*********************
*********************************************
然后,如果接口返回的是字符串应该怎么处理呢,于是我换了一个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类是从字符输入流中读取文本并缓冲字符,以便有效地读取字符,数组和行,具体解释参考:
还用了StringBuffer ,补充一个String,StringBuffer,StringBuilder三者的使用方法和区别:
















