1.IO流
以后开发中会遇到文件的上传和下载,都是需要用到IO流
咱们电脑上面所有的文件, 文档 音频 图片 视频 等都是可以进行读和写的。他们咋读的?
咋写的? 一下子就写到咱们项目中了吗?不是,依靠流的形式进行读 和写。很抽象
在读取的时候,会将咱们的文件(音频 视频 等)变成流的形式 一点一点来处理的 拆解开来出来的。
1.1缓冲的概念
看视频有点卡?暂停一下,加载缓冲一下。
快递:送到物流中转站,然后分批次的发。物流中转站就是缓冲的概念。
IO流 的本质就是对电脑的文件进行读和写的
计算机通过CPU内存读取磁盘上面的文件数据,一次读取1字节。但是可以加上缓冲的概念
每次读取4kb。效率会高点的,咱们可以测试一下。效率是不是确实高了。
1.2IO流的分类
咱们今天的学习紧紧围绕着:
1.从磁盘读取数据到内存(Java代码) 磁盘=》代码中 输入流
2.从内存(Java代码 String str = "狗蛋很帅")写入数据到磁盘某一个文件 代码=》磁盘 输出流
参照物:是内存,就是Java代码。
输入流: 从磁盘读取到内存
输出流:从内存写入到磁盘
I:
input: 输入 从磁盘读取数据到内存
使用input输入流的应用场景是啥:比如 磁盘上面有一个1.txt文件,将1.txt文件的内容读取到Java内存中。使用的是输入流。
输入流分为两种:
1.字节输入流 FileInputStream
2.字符输入流
输出流分为两种:
1.字节输出流 FileOutputStream
2.字符输出流
1.2.1字节输入流
讲一个新的知识点,你最起码先知道这个知识点能干嘛?
字节输入流:磁盘上面有一个1.txt文件,现在要将1.txt文件中的内容读取到内存(Java代码中)
Java给咱们封装好了类: FileInputStream 文件输入流(字节输入流)
package com.qfedu.a_fileinputstream;
import java.io.*;
public class Demo1 {
public static void main(String[] args) throws IOException {
//将磁盘上面的c:/aaa/1.txt文件这个数据读取到内存(Java)中
//加上缓冲的效果,这种很牛逼啊,将磁盘上面的东西弄到咱们Java代码中
//1.创建File对象,是咱们本地磁盘文件的一个File对象
//为啥要创建这个File对象?因为要读取1.txt这个文件的里面的
//内容,首先先找到文件再说啊。
File file = new File("c:/aaa/1.txt");
//2.创建文件字节输入流对象,来操作1.txt
//FileInputStream(File file)
//通过打开与实际文件的连接创建一个 FileInputStream ,
// 该文件由文件系统中的 File对象 file命名。
//将c:/aaa/1.txt文件转为字节输入流的形式,之后可以按照流的形式读取到内存中
FileInputStream fis = new FileInputStream(file);
//3.fileInputStream 这个流不具备缓冲的效果。
//但是加上缓冲的效果!!! 咋办?使用另外一个流
//BufferedInputStream
//A BufferedInputStream为另一个输入流添加了功能,即缓冲输入
//咋加?将FileInputStream传给BufferedInputStream 此时
//FileInputStream 就具备缓冲的功能了!!!
BufferedInputStream bis = new BufferedInputStream(fis);
//使用字节缓冲流操作咱们的1.txt
//当创建BufferedInputStream时,
// 将创建一个内部缓冲区数组,这个数组是什么数据类型的?
//5.创建一个缓冲区的数组 byte 为啥是byte类型的数组
//因为是字节输入流 所以是byte byte 就是字节
//当缓冲数组是2个字节的时候,每次读取的时候存到缓冲数组中,只存2个字节
//发现数据一次性读取不完?咋办?咋解决?
byte[] buf = new byte[1024 * 4];//缓冲区4096字节
//现在数组是空的
//6.读取数据
//public int read(byte[] b)
// throws IOException
//从输入流读取一些字节数,并将它们存储到缓冲区b 。
// 实际读取的字节数作为整数返回
//将1.txt文件的内容 读取到缓冲数组中 buf 里面
//之前buf是一个空的数组,现在通过read方法将1.txt文件中的
//内容读取到了 buf空的数组中
// int read = bis.read(buf);
// System.out.println(read);
//6.调用read方法之后,buf 数组就有值了 能不能打印一下
//借助于String类来展示byte数组的内容
//buf 给字符串 为了展示字节数组中的内容的
//0 偏移量 buf这个数组中从哪开始取值 从下标为0 的地方取值
//read 取6个
//如果b的长度为零,则不会读取字节并返回0 ;
// 否则,尝试读取至少一个字节。
// 如果没有字节可用,因为流在文件末尾,则返回值-1
int length = -1;
//如果length=-1 那么就会到达流的末尾 就证明后面没哟数据
//循环结束,不要再读了
while ((length = bis.read(buf)) != -1) {
//循环4次
System.out.println("狗蛋");
//数组中的东西要展示出来 使用String
//将磁盘上面的内容 读取到 =》 内存 buf
System.out.println(new String(buf, 0, length));
}
//7.流是需要关闭的
bis.close();
fis.close();
}
}
知道字节输入流是干嘛的?可以将磁盘上面的某一个文件读取到Java代码中(内存)
固定的写法!!!但是你得知道每行代码是啥意思
package com.qfedu.a_fileinputstream;
import java.io.*;
public class Demo2 {
public static void main(String[] args) throws IOException {
//1.创建File对象
File file = new File("c:/aaa/7.txt");
//2.创建字节输入流,将7.txt扔给 fis对象了
FileInputStream fis = new FileInputStream(file);
//3.创建字节缓冲流, fis又扔给 bis了 现在咱们的数据在bis里面了
BufferedInputStream bis = new BufferedInputStream(fis);
//4.声明一个缓冲数组,将bis里面的数据读取出来 赋值给这个
//缓冲数组
byte[] buf = new byte[4 * 1024];//4096字节
//5.开始使用read方法进行读取,读取的时候使用while循环
//如果7.tx文件中的字节数超过了4096个字节,需要使用循环,再次的读取
int length;
//现在数据都在bis里面,咱们要把bis里面的数据读取buf这个数组中
while ((length = bis.read(buf)) != -1) {
//byte数组有值以后,咱们可以打印一下看看byte数组中的数据
System.out.println("循环执行");
//将字节数组转为字符串,的目的是为了看一下字节数组中的数据
//并没有实际的意义
System.out.println(new String(buf, 0, length));
}
//6.关闭流
bis.close();
fis.close();
}
}
案例:读取你们盘符下面的一个txt文档,打印在idea控制台。写完以后,将代码直接发到扣扣群里
好好想想这个流程是咋走的?
想象咱们班门口放了一袋大米(就是磁盘的本地文件,文件的内容),现在我要求把门口给我运到我的讲台(Java的内存),不能使用袋子直接搬运?
你们咋解决?可以找一个杯子(就是缓冲数组)。一杯子一杯子的运。能接受不?
比如磁盘上面的380字节的数据, 咋把读取到内存中? 弄一个缓冲数组,每次读取3字节
BufferedInputStream 其实FileInputStream其实一次读取一个字节的,但是使用BufferedInputStream 了之后,可以搞一个缓冲的数组,一次可以读取多个,那么循环次数就会减少,代码的效率就会提升。循环1000遍 和循环1遍的效率 循环1遍的效率,下午演示 加缓冲流和不加缓冲流哪个效率高。
abcdefg
每次读取3个字节
第一次 abc
while (length = 3 != -1) { true
sout(abc)
}
第二次 def
while (length = 3 != -1) {true
sout(def)
}
第三次
while ((length = 1)!= -1 ) {true
sout(g)new String(buf, 0, 1)
}
当到达文件的末尾的时候, 后面没有东西了,文件的末尾了,到达流的末尾了。返回值是-1
while ((length = -1) != -1) { false 循环结束
sout(abc)
}
1.2.2字节输出流
将Java中数据写入到磁盘上面
内存-》磁盘 输出流
讲什么案例? 比如Java代码中有一个字符串 String str = "唉,愁人";将这个字符串写入磁盘某一个文件中
FileOutputStream
package com.qfedu.b_fileoutstream;
import java.io.*;
public class Demo1 {
public static void main(String[] args) throws IOException {
//将Java代码中的一个字符串写入到咱们的磁盘的某一个文件中
//1.创建File对象,文件路径对象 告知编译器要把数据写到哪个文件中
File file = new File("c:/aaa/2.txt");
//2.实例化字节输出流对象
FileOutputStream fos = new FileOutputStream(file);
//3.对FileOutputStream 加上缓冲的效果
//猜测一下 FileInputStream所对应的缓冲流 BufferedInputStream
//FileOutputStream 所对应的缓冲流 BufferedOutputStream
BufferedOutputStream bos = new BufferedOutputStream(fos);
//4.将一个字符串写入到磁盘中
String str = "大家再等我一天,后天见面!!!";
// write(byte[] b)
//咱们的参数是字节数组,咋办?str是一个字符串 但是参数是一个字节数组,咋解决?
//str是字符串 不能强转 将字符串转为字节数组,没有讲过!!!
//将str字符串转为字节数组
byte[] bytes = str.getBytes();
bos.write(bytes);
//5.写完以后一定要刷新流然后关闭流
bos.flush();//没有实际的意义,可以不写的
//6.关闭流
//在输入流的时候一定是先开的后关,后开的先关
bos.close();
fos.close();
}
}
package com.qfedu.b_fileoutstream;
import java.io.*;
public class Demo2 {
public static void main(String[] args) throws IOException {
//将内容写入到3.txt文件中
File file = new File("c:/aaa/3.txt");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
//开始写了
//void write(int b);
//传参是一个int类型,但是真正写入的时候是int类型所对应的字符
bos.write(97);//开发不用!!!
bos.flush();
bos.close();
fos.close();
}
}
package com.qfedu.b_fileoutstream;
import java.io.*;
public class Demo3 {
public static void main(String[] args) throws IOException {
File file= new File("c:/aaa/4.txt");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
String str = "abcdefg";//7个字节
//write(byte[] b,int off,int len)
byte[] bytes = str.getBytes();
//写一个到缓冲流 从0开始偏移 写入2个字节
//可以借助于循环往里面写!!!
bos.write(bytes, 0, 2);//ab
bos.write(bytes, 2, 2);//cd
bos.write(bytes, 4, 2);//ef
bos.write(bytes, 6, 1);//ef
bos.flush();
bos.close();
fos.close();
}
}
package com.qfedu.b_fileoutstream;
import java.io.*;
public class Demo4 {
public static void main(String[] args) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("c:/aaa/4.txt")));
bos.write("xixida".getBytes());
bos.flush();
bos.close();
}
}
针对于咱们的讲的字节输入流和字节输出流来一个案例
将c:/bbb文件夹下面一个视频 (源文件) 复制到c:/aaa文件夹下面
思路:将bbb文件夹下面的视频先读取到内存,然后再从内存写入磁盘aaa文件夹下面
缓冲数组 byte[] buf = new byte[4];
视频由字节组成的 一次读取 4096字节 直到最后一次 不一定4096字节
最后一次3456 字节 write(buf, 0, 3456)
相当与将所有字节重新拼接了一下
读取的时候每次读取4096个放到咱们的缓冲数组中,然后立马写入另外磁盘文件下面
直到读取完,写入完即可
qwertyuiopasd 13字节
//第一循环
while ((length=bis.read(buf)) != -1) {//buf 里面 有 qwer
bos.write(buf, 0, 4); 文本里面有 qwer
}
第二次循环
while ((length=bis.read(buf)) != -1) {//buf 里面 有 tyui
bos.write(buf, 0, 4); 文本里面有 qwertyui
}
第三次循环
while ((length=bis.read(buf)) != -1) {//buf 里面 有 opas
bos.write(buf, 0, 4); 文本里面有 qwertyuiopas
}
第四次循环
while ((length=bis.read(buf)) != -1) {//buf 里面 有 d
bos.write(buf, 0, 1); 文本里面有 qwertyuiopasd
}
package com.qfedu.c_io;
import java.io.*;
public class Demo1 {
public static void main(String[] args) throws IOException {
copyVideo1();
}
//现在咱们讲的这个是带有缓冲的,看看带有缓冲视频的复制的耗时多少 2610ms
//写一个方法将bbb文件夹下面的3.map4格式的视频复制到aaa文件夹下面
//用带缓冲的,效率高
public static void copyVideo () throws IOException {
//获取系统的当前的毫秒数
long start = System.currentTimeMillis();
//1.创建字节缓冲输入流 将磁盘的数据读取到内存
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("c:/bbb/3.mp4")));
//2.创建字节缓冲输出流 从内存写入到的文件 源文件是mp4格式,写入到的也必须是mp4格式的
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("c:/aaa/goudan.mp4")));
//3.准备一个字节数组 缓冲区数组
byte[] buf = new byte[4 * 1024];//代码走到这一步是空的
//4.从磁盘读取数据到内存
int length = -1;
//这个视频很大,依靠循环来读取,每次读4 * 1024字节
while ((length = bis.read(buf)) != -1) {//从磁盘上读取数据到buf缓冲数组中
//每次循环 buf数组中有值,顺便依靠循环写入到磁盘
System.out.println("qwer");
bos.write(buf, 0, length);
}
//5.关闭流
bos.close();
bis.close();
//复制完以后再得到一个毫秒数,相减 就得到复制一个视频需要的时间
long end = System.currentTimeMillis();
System.out.println(end - start);
}
//下面来=看一下不带缓冲的流的读写
public static void copyVideo1 () throws IOException {
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream(new File("c:/bbb/3.mp4"));
FileOutputStream fos = new FileOutputStream(new File("c:/aaa/goudan.mp4"));
int length = -1;
while ((length = fis.read()) != -1) {
fos.write(length);
}
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
1.2.3字符输入流【非重点】
也是输入流,将磁盘的某一个文件读取到内存
FileReader:
是一个阅读字符文件的便利类,是专门处理字符文件的,比如txt文件。音频视频图片
不能使用这个流。
是从字节流到字符流的桥:它读取字节,并使用指定的
charset
将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集牵涉到解码,底层是字节流,但是会解码为字符。如果解码失败就意味着咱们读取失败了
一般不会使用字符流操作图片 音频 视频等,因为牵涉到解码。会出问题!!!
开发一般使用字节流!!!
package com.qfedu.d_FileReader;
import java.io.*;
public class Demo1 {
public static void main(String[] args) throws IOException {
//将c盘下面的aaa文件夹下面的1.txt文件的内容 读取到Java内存中
//和字节流一模一样
//1.新建一个File对象
File file = new File("c:/aaa/1.txt");
//2.新建FileReader 对象 字符输入流
FileReader fr = new FileReader(file);
//3.FileRFeader没有缓冲的效果,可以加上缓冲的效果
//杂加?BufferedReader
BufferedReader br = new BufferedReader(fr);
//4.字节的缓冲数组 是字节
//字符的缓冲数组 是字符
char[] cbuf = new char[4];//缓冲区是4个字符
//这个方法是用的!!!
//5. read(char[] cbuf) 将文件内容读取到字符数组中(缓冲区)
int length = -1;
while ((length = br.read(cbuf)) != -1) {
System.out.println(length);
//6.将数组展示到控制台
System.out.println(new String(cbuf, 0, length));
}
//7.关闭流
br.close();
fr.close();
}
}
package com.qfedu.d_FileReader;
import java.io.*;
public class Demo2 {
public static void main(String[] args) throws IOException {
//将c盘下面的aaa文件夹下面的1.txt文件的内容 读取到Java内存中
//和字节流一模一样
//1.新建一个File对象
File file = new File("c:/aaa/1.txt");
//2.新建FileReader 对象 字符输入流
FileReader fr = new FileReader(file);
//3.FileRFeader没有缓冲的效果,可以加上缓冲的效果
//杂加?BufferedReader
BufferedReader br = new BufferedReader(fr);
//这个方法不用!!!
//5.int read() 一次读取一个字符,返回值是字符对应的assic码值
int length = -1;
while ((length = br.read()) != -1) {
System.out.println(length);
}
//7.关闭流
br.close();
fr.close();
}
}
package com.qfedu.d_FileReader;
import java.io.*;
public class Demo3 {
public static void main(String[] args) throws IOException {
//将c盘下面的aaa文件夹下面的1.txt文件的内容 读取到Java内存中
//和字节流一模一样
//1.新建一个File对象
File file = new File("c:/aaa/1.txt");
//2.新建FileReader 对象 字符输入流
FileReader fr = new FileReader(file);
//3.FileRFeader没有缓冲的效果,可以加上缓冲的效果
//杂加?BufferedReader
BufferedReader br = new BufferedReader(fr);
//4.字节的缓冲数组 是字节
//字符的缓冲数组 是字符
char[] cbuf = new char[4];//缓冲区是4个字符
//这个也不用!!!
//5. read(char[] cbuf, 0, length) 将文件内容读取到字符数组中(缓冲区)
int length = -1;
while ((length = br.read(cbuf, 0, 2)) != -1) {
System.out.println(length);
//6.将数组展示到控制台
System.out.println(new String(cbuf, 0, length));
}
//7.关闭流
br.close();
fr.close();
}
}
package com.qfedu.d_FileReader;
import java.io.*;
public class Demo4 {
public static void main(String[] args) throws IOException {
//将c盘下面的aaa文件夹下面的1.txt文件的内容 读取到Java内存中
//和字节流一模一样
//1.新建一个File对象
File file = new File("c:/aaa/1.txt");
//2.新建FileReader 对象 字符输入流
FileReader fr = new FileReader(file);
//3.FileRFeader没有缓冲的效果,可以加上缓冲的效果
//杂加?BufferedReader
BufferedReader br = new BufferedReader(fr);
//一次读取一行数据,r如果到达行的末尾就返回就是一个null
// String s = br.readLine();
// System.out.println(s);
// String s1 = br.readLine();
// System.out.println(s1);
String str;
//有可能会用的
while ((str = br.readLine()) != null) {
System.out.println(str);
}
//7.关闭流
br.close();
fr.close();
}
}
1.2.4z字符输出流
案例:
将一个字符串类型的数据写入到磁盘中
FileWriter
写字符文件一个便利类
package com.qfedu.e_fileWriter;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
//从内存写入数据到磁盘的文件中
File file = new File("c:/aaa/2.txt");
FileWriter fw = new FileWriter(file);
//加缓冲流
BufferedWriter bw = new BufferedWriter(fw);
//viod writer(char[] cbuf, int off, int length);
String str = "今天的天气真的太热了";
//将字符串转为char数组
char[] chars = str.toCharArray();
//将chars数组中 从0 开始 取3个字符写入到缓冲流中
bw.write(chars, 3, 3);
bw.close();
fw.close();
}
}
package com.qfedu.e_fileWriter;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class Demo2 {
public static void main(String[] args) throws IOException {
//从内存写入数据到磁盘的文件中
File file = new File("c:/aaa/2.txt");
FileWriter fw = new FileWriter(file);
//加缓冲流
BufferedWriter bw = new BufferedWriter(fw);
//viod writer(char[] cbuf);
String str = "今天的天气真的太热了";
//将字符串转为char数组
char[] chars = str.toCharArray();
//将chars数组中,直接将字符数组中的所有的数据写到缓冲流中
bw.write(chars);
bw.close();
fw.close();
}
}
package com.qfedu.e_fileWriter;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class Demo3 {
public static void main(String[] args) throws IOException {
//从内存写入数据到磁盘的文件中
File file = new File("c:/aaa/2.txt");
FileWriter fw = new FileWriter(file);
//加缓冲流
BufferedWriter bw = new BufferedWriter(fw);
//viod writer(String str);
String str = "对方网络不佳!!!";
//viod writer(String str, int off, int length);
//bw.write(str);
//bw.write(str, 1, 4);
bw.write("呵呵");
bw.newLine();//写完呵呵以后换行写 功能代码 起到一个换行的作用
bw.write("哈哈");
bw.newLine();
bw.write("嘻嘻");
bw.close();
fw.close();
}
}
综合案例:
复制一本小说到另外一个盘符下面
package com.qfedu.e_fileWriter;
import java.io.*;
public class Demo4 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(new File("c:/bbb/DiBa.txt")));
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("c:/aaa/sb1.txt")));
int length = -1;
char[] cbuf = new char[4 * 1024];
while ((length = br.read(cbuf)) != -1) {
bw.write(cbuf, 0, length);
}
bw.close();
br.close();
}
}