勿以恶小而为之,勿以善小而不为--------------------------刘备

劝诸君,多行善事积福报,莫作恶

上一章简单介绍了File类的使用(一),如果没有看过,​​请观看上一章​​

一. OutputStream

OutputStream 是字节输出流, 用于从程序中往文件里写入内容。 常常使用其子类, FileOutputStream.

一.一 OutputStream 接口方法

方法名

作用

abstract void write(int b)

写入单个字节,写入内容为 b

void write(byte[] b)

写入整个字节数组, 写入内容为 字节数组

void write(byte[] b, int off, int len)

写入字节数组,从off 到off+len 的内容

void close()

关闭此输出流并释放与此流相关联的任何系统资源。

void flush()

刷新此输出流并强制任何缓冲的输出字节被写出

一定要关闭流。

一.二 FileOutputStream 类

对于文件的字节操作,常常使用 FileOutputStream 类。

一.二.一 构造方法

一.二.一.一 方法

方法

作用

FileOutputStream(File file)

传入文件, 输出时重写文件内容

FileOutputStream(File file, boolean append)

传入文件,append为true时,追加文件内容,为false时,重写文件内容。

FileOutputStream(String name)

传入文件的路径,相对路径和绝对路径均可以,输出时重写文件内容

FileOutputStream(String name, boolean append)

传入文件的路径,append为true时,追加文件内容,为false时,重写文件内容

实际上,常用这两种方式,一种是传入文件,另外一种是传入文件路径。

如果传入的文件不存在的话,那么构造时会创建该文件。

老蝴蝶建议, 传入文件。

一.二.一.二 演示
@Test
public void conTest() throws Exception{
//有两种,一种是传入文件File, 一种是传入路径字符串

File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

//传入File,重写文件

OutputStream outputStream=new FileOutputStream(file);

//传入File, 追加文件
// OutputStream outputStream=new FileOutputStream(file,true);


String path="E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello.txt";
//传入路径, 重写文件
OutputStream outputStream1=new FileOutputStream(path);
//传入路径,追加文件
// OutputStream outputStream1=new FileOutputStream(path,true);


}

一.二.二 写入和关闭方法

重写父类的方法, 主要是 write() 方法。

字节流InputStream和OutputStream(二)_Java的字节流操作

一.三 OutputStream 写入文件

一.三.一 write(int b) 单个写入

写入int 类型,如果写入的是字符串的话,需要将字符串转换成字节数组,然后遍历字节数组,一个个写入。

@Test
public void writeContentTest() throws Exception{
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

//这个文件不存在
OutputStream outputStream=new FileOutputStream(file);

//输入单个内容
outputStream.write(100);

//输入单个内容
outputStream.write((int)'a');



//输入数组
String str="Hello,My Name is TwoButterfly\r\n";


//一次次写入
byte[] bytes=str.getBytes("UTF-8");

//一个一个写入
for(byte b:bytes){
//byte 类型会自动转换成 int 类型
outputStream.write(b);
}

System.out.println("写入内容成功");
outputStream.close();

}

运行程序,查看文件内容

字节流InputStream和OutputStream(二)_InputStream_02

会将100 转换成字节, 转换后为 d. (97+3, 97为小写字母a)

会发现,当写入字符串时,需要一个一个的写,效率太低。 能不能直接写入字符串呢?

一.三.二 write(byte[] bytes) 字节数组形式写入

当写入字符串时,因为是字节输出,所以是不能直接写入字符串的, 但却可以直接写入字节数组。 就是将一个个字节先封装起来,封装成一个字节数组,然后以字节数组为单位,进行写入。 建议都采用写字节数组的形式。

@Test
public void writeContent2Test() throws Exception{
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

//不是追加,是重写。
OutputStream outputStream=new FileOutputStream(file);

//输入字符串

String str="Hello,My Name is TwoButterfly";


//将字符串转换成字节数组
byte[] bytes=str.getBytes("UTF-8");

//写入字节数组, 也可以 后面添加 0, len 形式的。
outputStream.write(bytes);

System.out.println("写入文件成功");
outputStream.close();

}

运行程序,查看内容

字节流InputStream和OutputStream(二)_字节流复制文件_03

发现,将以前的内容,先删除,再进行写入。 那么,能不能保留以前的内容呢?

一.三.三 追加内容,构造方法时令append 参数为true

在构造方法 FileOutputStream 时,令参数 append为true 即可。

@Test
public void writeContent3Test() throws Exception{
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

//令参数为true,追加内容
OutputStream outputStream=new FileOutputStream(file,true);

//输入字符串, 追加内容
String str="Thank you";
//将字符串转换成字节数组
byte[] bytes=str.getBytes("UTF-8");

//写入字节数组
outputStream.write(bytes);

System.out.println("写入文件成功");
outputStream.close();

}

运行程序:

字节流InputStream和OutputStream(二)_字节流_04

会发现,格式非常不好,都连在一起了, 能不能换行呢?

一.三.四 换行,写入 \r\n

换行,只需要写入换行符就可以了。 换行符是 \r\n

@Test
public void writeContent4Test() throws Exception{
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

//令参数为true,追加内容
OutputStream outputStream=new FileOutputStream(file,true);

//输入字符串, 追加内容,并且换行
String str="\r\n I change Line"+"\r\n I change Line too";
//将字符串转换成字节数组
byte[] bytes=str.getBytes("UTF-8");

//写入字节数组
outputStream.write(bytes);

System.out.println("写入文件成功");
outputStream.close();

}

运行程序:

字节流InputStream和OutputStream(二)_字节流复制文件_05

现在写入的都是英文字符,能不能写入我们伟大的中国汉字呢?

一.三.五 写入汉字

@Test
public void writeContent5Test() throws Exception{
File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

//这个文件不存在
OutputStream outputStream=new FileOutputStream(file,true);

//输入汉字

String str="\r\n你好,我是两个蝴蝶飞";


//写入数组
byte[] bytes=str.getBytes("UTF-8");

//换一种形式
outputStream.write(bytes,0,bytes.length);
outputStream.close();

}

运行程序:

字节流InputStream和OutputStream(二)_字节流复制文件_06

正常写入汉字。 能够将内容写入到文件里面,那么就可以将文件内容读取出来。

二. InputStream

InputStream 是字节输入流, 用于读取文件中的内容。常常使用其子类 FileInputStream.

二.一 InputStream 接口方法

方法

作用

abstract int read()

只读取一个内容, 并且返回该内容

int read(byte[] b)

一次读取多个内容,并且将内容放置到 b 字节数组里面。返回读取的长度

int read(byte[] b, int off, int len)

一次读取多个内容,读的内容是从 off 到 off+len, 然后将内容放置到b 字节数组里面

void close()

关闭此输入流并释放与流相关联的任何系统资源。

二.二 FileInputStream

二.二.一 构造方法

二.二.一.一 方法

方法

作用

FileInputStream(File file)

放置进去文件

FileInputStream(String name)

放置进去文件的路径

可以传入文件,也可以传入文件的路径, 注意,文件必须要存在,否则会抛出 FileNotFoundException 异常。

老蝴蝶建议,传入文件的形式。

二.二.一.二 演示
@Test
public void conTest() throws Exception{

File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello.txt");
//传入文件
InputStream inputStream=new FileInputStream(file);


String path="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello.txt";
//传入文件的路径
InputStream inputStream1=new FileInputStream(path);
}

二.三 InputStream 读取文件

二.三.一 read() 一个一个读取

@Test
public void read1Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

InputStream inputStream=new FileInputStream(file);

//读一个,需要转换成 char 字符进行展示
System.out.println("第一个:"+(char)inputStream.read());

System.out.println("第二个:"+(char)inputStream.read());

System.out.println("第三个:"+(char)inputStream.read());


int r=-1;
byte[] b=new byte[(int)file.length()];
int i=0;
//需要一个个放置到字节数组里面
while((r=inputStream.read())!=-1){
b[i]=(byte)r;
i++;
}
System.out.println("读出文件内容:"+new String(b,0,i));
inputStream.close();
}

运行程序,控制台打印输出:

字节流InputStream和OutputStream(二)_字节流复制文件_07

发现,中文目前可以正常的打印出来。

注意: read() 读取之后,无法再重新返回去读。

读内容是一个一个的读,那么能不能读完之后,封装到一个数组里面呢, 这样便于获取?

二.三.二 read(byte[] b ) 读取内容后封装到数组里面

@Test
public void read2Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

InputStream inputStream=new FileInputStream(file);

//根据文件大小,设置字节数组的大小
byte[] bytes=new byte[(int)file.length()];

//读取之后,封装到数组里面
inputStream.read(bytes);

System.out.println("读出内容:"+new String(bytes));
inputStream.close();
}

控制台打印输出:

字节流InputStream和OutputStream(二)_字节流复制文件_08

中文也可以正常的显示出来,换行也能显示。

但发现有一个问题,创建字节数组时,用的是 new byte[(int)file.length()]; 也就是先统计一下文件的大小,然后根据文件大小,去构建数组,需要先查询一下。 这样效率会低一些。

二.三.三 read(byte[] b) 数组长度设置为 1024

@Test
public void read3Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

InputStream inputStream=new FileInputStream(file);

//设置长度 为1024
byte[] bytes=new byte[1024];

inputStream.read(bytes);
System.out.println("读出内容:"+new String(bytes));
inputStream.close();
}

运行程序:

字节流InputStream和OutputStream(二)_字节流复制文件_09

会发现,后面有很多很多的空格, 浪费了 bytes 数组的空间。 可以利用 read(bytes) 方法的返回值进行控制。

二.三.四 read(byte[] b) 返回值

@Test
public void read4Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

InputStream inputStream=new FileInputStream(file);

//设置长度 为1024
byte[] bytes=new byte[1024];

//返回的内容,就是读取的长度
int len=inputStream.read(bytes);
System.out.println("读出内容:"+new String(bytes,0,len));
inputStream.close();
}

运行程序,控制台打印输出:

字节流InputStream和OutputStream(二)_字节流复制文件_10

你以为这样就成功了吗? 不,还没有。 现在我们固定长度为 1024, 也就是可以读取 1KB的内容, 但是如果长度超过了 1KB, bytes字节数组是放不下的,会造成文件的内容读取不全的, 那该怎么办呢?

老蝴蝶可以演示一下,这种效果。 我们可以设置长度为 10, 这个长度肯定小于文件的大小

@Test
public void read5Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

InputStream inputStream=new FileInputStream(file);

//设置长度 为10
byte[] bytes=new byte[10];
//返回的内容,就是读取的长度
int len=inputStream.read(bytes);
System.out.println("读出内容:"+new String(bytes,0,len));
inputStream.close();
}

这个时候,运行程序:

字节流InputStream和OutputStream(二)_InputStream_11

会发现,文件内容没有读全,只读取了前10个字节。

要想全部读完,我们可以设置循环读取。

二.三.五 read(byte[]b ,int offset, int len) 循环读取内容

@Test
public void read6Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

InputStream inputStream=new FileInputStream(file);

StringBuilder sb=new StringBuilder();


byte[] bytes=new byte[10];

int len=-1;

while((len=inputStream.read(bytes))!=-1){
//依次读取的内容
String temp=new String(bytes,0,len);
sb.append(temp);
}
System.out.println("输出读取的内容:"+sb.toString());
inputStream.close();
}

控制台打印输出:

字节流InputStream和OutputStream(二)_Java的字节流操作_12

可以读取全部的内容信息,但是发现,产生了中文乱码的问题。

这是由于在读取中文时,准确地说在读取到 汉字’个’时, 正好将个这个词的字节给分开了, 一半在 bytes[9], 另一半在下一次bytes数组的 的bytes[0]位置。

如果将字节数组bytes的长度扩大,如扩大到 1024, 就不会产生乱码问题了。

字节流InputStream和OutputStream(二)_OutputStream_13

二.四 字节读取时,中文乱码问题

在fileSrc目录下,新创建一个 rz.txt 文件, 填充内容为:

你好我是两个蝴蝶飞你好我是两个蝴蝶飞

我们读取这个文件,来演示中文乱码问题。

二.四.一 字节数组长度为奇数时

字节数组为奇数时,很容易造成中文乱码。

@Test
public void read7Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"rz.txt");

InputStream inputStream=new FileInputStream(file);

StringBuilder sb=new StringBuilder();


byte[] bytes=new byte[5];

int len=-1;

while((len=inputStream.read(bytes))!=-1){
//依次读取的内容
String temp=new String(bytes,0,len);
sb.append(temp);
}
System.out.println("输出读取的内容:"+sb.toString());
inputStream.close();
}

运行程序:

字节流InputStream和OutputStream(二)_InputStream_14

有很大概率,会将中文进行拆分。

这个例子时,将 byte[5] 转换成 byte[6],换成偶数时:

字节流InputStream和OutputStream(二)_InputStream_15

这个时候,就可能不乱码了。

二.四.二 字节数组长度不被3整除时

上面的字节长度为6时,不乱码,为10时就乱码了。

字节流InputStream和OutputStream(二)_字节流_16

为12时,就不乱码了,为 14时乱码了,为18时不乱码了。

老蝴蝶建议,接收的字节数组的长度最好是能被3整除的偶数。 读取时,为了方便,常常是1024的整数被。

所以,综合起来就是: 能同时被1024和3整除的偶数。

为了方便,老蝴蝶还是以 1024为主。

二.五 读取文件内容综合

@Test
public void read8Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"Hello3.txt");

InputStream inputStream=new FileInputStream(file);
//长度设置成1024
byte[] bytes=new byte[1024];

int len=-1;

while((len=inputStream.read(bytes))!=-1){
//依次读取的内容
System.out.println("读取的内容为:"+new String(bytes,0,len));
}
inputStream.close();
}

三. OutputStream和InputStream的应用: 文件复制

文件复制,实际上就是将 InputStream和 OutputStream 结合起来使用, 两者共同使用同一个 字节数组, InputStream将读出的内容批量写入到字节数组里面, OutputStream将字节数组批量写入到文件里面。

三.一 文件复制方法

/**
* 复制字节文件
* @param src 源文件
* @param desc 目标文件
* @return
*/
public static boolean copyBin(String src,String desc) throws Exception{

//定义两个文件
File srcFile=new File(src);

File descFile=new File(desc);

if(!srcFile.exists()||!srcFile.isFile()){
throw new RuntimeException("源文件不存在或者源文件是目录");
}

//定义InputStream 和 OutputStream

InputStream inputStream=new FileInputStream(srcFile);

OutputStream outputStream=new FileOutputStream(descFile);

//1M 1M的读取
byte[] bytes=new byte[1024];

int len=-1;

//将内容写入到 bytes字节数组里面
while((len=inputStream.read(bytes))!=-1){
//将bytes字节数组写入到文件里面
outputStream.write(bytes,0,len);
}

outputStream.flush();
//关闭流
outputStream.close();
inputStream.close();
return true;

}

三.二 测试文件复制

复制文件和复制图片类型都可以。

@Test
public void copyTest(){

//复制普通文件
/* String src="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"rz.txt";

String desc="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"rzcopy.txt";*/

//复制图片
String src="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"129.png";

String desc="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"129copy.png";


try {
CopyUtils.copyBin(src,desc);
System.out.println("文件复制成功");
} catch (Exception e) {
e.printStackTrace();
}
}

运行程序,查看文件系统

字节流InputStream和OutputStream(二)_字节流_17