此文目的:适当地选择java的 I / O流来复制文件。
像什么docx啊,什么xlsx呀的,都要插入图片视频什么的吧?那插入的这些东西,如果我们也用字符流来复制的话就会造成文件损坏。所以一般来说,除了txt文件用字符流(Reader 和 Writer,操作纯文本)外,其他的文件我建议用字节流来复制。
一、先来看一段在我学习时老师教的常规的复制文件方法
四步曲:(注:main方法抛 IO 异常)
//1.定义指针指向文件:
File fIn = new File("F:\\LCY\\LCY ujqi\\珍藏\\次仁央宗 - 为你等待.mp3");//得到文件
File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\次仁央宗 - 为你等待.mp3");//输出到哪个位置
//定义开始计时时间:
long start = System.currentTimeMillis();
//2.定义流指向指针:
InputStream is = new FileInputStream(fIn);//输入流
OutputStream os = new FileOutputStream(fOut);//输出流
//3.复制操作:
int k = 0;
while ((k = is.read()) != -1) {
os.write(k);
}
//4.关闭流
os.close();
is.close();
//6.定义结束计时时间:
long end = System.currentTimeMillis();
//看看所需要的时间:
System.out.println("复制文件成功。" +
"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
"用时:" + (end - start) + " ms");
注意(指针的解释):
我之前也学过 C++ ,里面有一个重要的知识点叫"指针"。
我的课本里解释:指针就是地址。
我的老师也说:还不知道指针?指针就是地址啊!!
当我学习 java 时,老师就讲到:在java里面没有指针这个说法。
当时我就在想:
那我们的像上面定义的这个 fIn / fOut 是什么?
它不是也是指向这个东西吗?
或者具体来说,它也是指向内存存储文件的这个空间呀?
后来我的理解是这样的:
不管是 C++ 也好,还是 java 也好,实际上都是有指针这个东西的。
指针应该这样理解:
指针不是地址,而是指向计算机地址的一个媒介。
计算机通过指针,可以访问这个地址。
由于 java 里没有指针这个说法,所以我所说的指针实际上是指:
通过这个定义的变量来访问这个文件。
好了,看看结果
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=49749:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:9 MB,用时:40654 ms
Process finished with exit code 0
如上,字节流复制文件,10M的东西,40秒,成功了。
上述代码的简写
计算机干的事情是:你告诉我想要什么,我给你。而且他没有思想,不会厌烦,相反他最喜欢干。举个例子:
p1:“给个箱子,我存钱!” 计算机:“double money = 0; 好,给!”
p2:“给些流,我要复制文件!” 计算机:“Reader、Writer、Stream,都在这,自己选!”
p3:“给个女朋友!” 计算机:“女朋友 女朋友 = new 女朋友("闭月羞花"); 好了,给你 new 个最好的,拿去,不用谢!”
看到没有?计算机很乐于去作这种你输入需求,我闭着眼睛从百宝箱里拿出来丢给你的这种事情,关键是他还不会出错。但我们就不一样。拿这个复制文件的流举个例子:
定义指针定义流 ~ 复制文件关闭流。。。
每次都是这几步,好,计算机给你流了。但当有一天,你忘记还给计算机了,那计算机的百宝箱是不是就缩水了?积少成多,最后造成的结果是不是百宝箱空空如也了?
为了解决我们这个有头无尾的坏毛病,大佬就在 JDK 7 时让虚拟机自动帮我们关闭流,好,例子在这
File fIn = new File("F:\\LCY\\LCY ujqi\\珍藏\\次仁央宗 - 为你等待.mp3");//得到文件
File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\次仁央宗 - 为你等待.mp3");//输出到哪个位置
long start = System.currentTimeMillis();//得到系统时间(开始计时)
try (
InputStream is = new FileInputStream(fIn);//输入流
OutputStream os = new FileOutputStream(fOut);//输出流
) {
int k = 0;
while ((k = is.read()) != -1) {
os.write(k);
}
}
long end = System.currentTimeMillis();//得到系统时间(结束计时)
System.out.println("复制文件成功。" +
"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
"用时:" + (end - start) + " ms");
如上所示,我们只需要写入一个try,把需要关闭的资源放到小括号(…)里面,把资源的操作放到大括号{…}里面,我们就可以大刀阔斧地使用资源了,你用完了,撂担子走人就好,计算机自己会把属于他的资源拿回来的。
因为这种办法简单,所以我下面就只使用这种办法来写了。
那么有个问题,这种方法只有10M的东西,就要40秒了哦!——“才40秒,没关系,哥有的是时间!” 好,1个G呢??
所以,正确地使用I / O流 来复制文件是学java中特别有必要的一件事。
二、接着,浅谈一下缓冲流
缓冲流是什么?
最近我们家晒谷子了,就就地取材用谷子举例吧
天要下雨了,我们要把谷子收到麻袋里。
那么字节流这样的,就相当于你一把谷子一把谷子地捧起来放到麻袋,那你还要不要你家的谷子?你品,你细品。
缓冲流就像是你拿一个铲子,一铲一大把,这效率是不是就变高了?
好了,直接上代码
File fIn = new File("F:\\LCY\\LCY ujqi\\珍藏\\次仁央宗 - 为你等待.mp3");//得到文件
File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\次仁央宗 - 为你等待.mp3");//输出到哪个位置
long start = System.currentTimeMillis();//得到系统时间(开始计时)
try (
InputStream is = new FileInputStream(fIn);//输入流
OutputStream os = new FileOutputStream(fOut);//输出流
BufferedInputStream bis = new BufferedInputStream(is);//缓冲输入流
BufferedOutputStream bos = new BufferedOutputStream(os);//缓冲输输出流
) {
byte[] bytes = new byte[1024];//长度数组,每次读 1KB 的长度
int k = 0;
while ((k = bis.read(bytes)) != -1) {
bos.write(bytes, 0, k);
/*
bos.write(
bytes: 写什么
0: 从哪开始写
k: 写多少
)
*/
}
}
long end = System.currentTimeMillis();//得到系统时间(结束计时)
System.out.println("复制文件成功。" +
"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
"用时:" + (end - start) + " ms");
上述代码中,我们比之前多了try(…)代码块的两个缓冲流、一个长度的数组,而且复制操作中的 k = is.read() 改成了 k = bis.read(bytes) ,下面的写操作 os.write(k) 改成了 bos.write(bytes, 0, k) 。
那么这就是一个完整的缓冲流复制文件了,上重点
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=65181:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:9 MB,用时:64 ms
Process finished with exit code 0
您看,一样的文件,字节流要40654ms,缓冲流64ms,这种效率,您家谷子是不是可以免受大雨的 “社会毒打” 之苦了?
但是,缓冲这东西嘛,并不是说越大越好。就像你铲谷子,1L 容量的太慢,但是 100L 的你拿的起吗?你装进铲子里头都费劲,到头来是不是更慢?好嘞,那我们就来测试一下,对于计算机来说,究竟用多大容量的 “铲子” 来铲谷子是最合适的。
我们来个大文件玩玩,我电脑里目前最大的文件就是这个,1.5G,差不多够用了,就不搞镜像文件这东西了
先来个缓冲 1KB
File fIn = new File("F:\\LCY\\LCY tcpn\\temp\\阅\\cut_012203_164_0_1.mp4");
File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\cut_012203_164_0_1.mp4");
long start = System.currentTimeMillis();//得到系统时间(开始计时)
try (
InputStream is = new FileInputStream(fIn);//输入流
OutputStream os = new FileOutputStream(fOut);//输出流
BufferedInputStream bis = new BufferedInputStream(is);//缓冲输入流
BufferedOutputStream bos = new BufferedOutputStream(os);//缓冲输输出流
) {
byte[] bytes = new byte[1024];//长度数组,每次读 1KB 的长度
int k = 0;
while ((k = bis.read(bytes)) != -1) {
bos.write(bytes, 0, k);
}
}
long end = System.currentTimeMillis();//得到系统时间(结束计时)
System.out.println("复制文件成功。" +
"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
"用时:" + (end - start) + " ms");
结果看下面
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54078:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:39011 ms
Process finished with exit code 0
好了,要39秒。
下一个,1MB
不啰嗦,就只改长度数组那一行
byte[] bytes = new byte[1024 * 1024];//每次读 1MB 的长度
得到结果:
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54078:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:15335 ms
Process finished with exit code 0
看,15秒。
下一个,10M
byte[] bytes = new byte[1024 * 1024 * 10];//每次读 10MB 的长度
结果:
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54508:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:13824 ms
Process finished with exit code 0
14秒,几乎没区别了。
下一个,20M
byte[] bytes = new byte[1024 * 1024 * 20];//每次读 20MB 的长度
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54567:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:21226 ms
Process finished with exit code 0
好的,21秒。那么可以看到:到 20M 缓冲时,复制的效率慢下来了,那也就是说,速度最快的缓冲是在 [1MB, 20MB]区间内。
我们用二分法来看看。
a.5MB
byte[] bytes = new byte[1024 * 1024 * 5];//每次读 5MB 的长度
结果如下
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54755:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:15740 ms
Process finished with exit code 0
b.5M 的速度和 1M 的差不多,再看看 15M 的
byte[] bytes = new byte[1024 * 1024 * 15];//每次读 15MB 的长度
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54827:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:15549 ms
Process finished with exit code 0
也是15秒,那也就是说 速度最快的缓冲就是 10M 的了。但这只是理论上的。实际上,计算机瞬时读取系统时间时也是有误差的,就那么一瞬间,几十毫秒就溜过去了。而在我之前测的一个文件是600M,测了5次,总结出速度是 5MB > 10MB > 1MB。
所以,缓冲流中,我推荐的缓冲数组长度是 [5MB, 10MB] 这个区间。
那我一直在这里做,我还是个现代人吗?现在的人工智能跟我无缘了吗?java我待你不薄,你为什么要这样对我????
你放心,社会没有抛弃你,java也没有抛弃你,java也有人工智能的,不要你面朝黄土背朝天地干!介绍两个,异步和通道。
异步是什么?
异步就是,你雇了个人,在那帮你收谷子,你坐在旁边守着。如果你上三急去上厕所,恰好他在这个时间段就把谷子收好了,那他就坐在那里,等你上完厕所回来给他结工资。
好了,介绍完了,拿刀来!!
File fIn = new File("F:\\LCY\\LCY tcpn\\temp\\阅\\cut_012203_164_0_1.mp4");
File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\cut_012203_164_0_1.mp4");
long start = System.currentTimeMillis();//得到系统时间(开始计时)
//复制操作
Files.copy(
fIn.toPath(),//输入指针的路径
fOut.toPath(),//输出指针的路径
StandardCopyOption.COPY_ATTRIBUTES,//复制属性
StandardCopyOption.REPLACE_EXISTING//替换现有的
);
long end = System.currentTimeMillis();//得到系统时间(结束计时)
System.out.println("复制文件成功。" +
"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
"用时:" + (end - start) + " ms");
可以看到,这个异步操作,就是把try(…){…}的东西换成了一句话 (我这里为了易看,就将每个参数写成了一行) 。好,看结果
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=56947:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:40393 ms
Process finished with exit code 0
40秒,和缓冲流的 1KB 缓冲不差多少。
再说一下通道。
所谓通道,就是你雇个人干活,你不用在旁边守着了,回去打你王者荣耀,吃你鸡,追你剧去。啥?你问他干完活了后怎么给钱?微信呀,说好的面向现代化呀!!!
二爷青龙偃月刀在此!
File fIn = new File("F:\\LCY\\LCY tcpn\\temp\\阅\\cut_012203_164_0_1.mp4");
File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\cut_012203_164_0_1.mp4");
long start = System.currentTimeMillis();//得到系统时间(开始计时)
//复制操作
try (
FileInputStream fis = new FileInputStream(fIn);//输入流
FileOutputStream fos = new FileOutputStream(fOut);//输出流
) {
//各自获取各自的可读可写通道
FileChannel fcRead = fis.getChannel();
FileChannel fcWrite = fos.getChannel();
fcRead.transferTo(0, fcRead.size(), fcWrite);//transferTo:将该通道的文件字节传到可写通道
}
long end = System.currentTimeMillis();//得到系统时间(结束计时)
System.out.println("复制文件成功。" +
"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
"用时:" + (end - start) + " ms");
好了,结果如下
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=57881:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:40757 ms
Process finished with exit code 0
也是40秒,没多少大变化。那我要他何用?重点是你不需要亲力亲为了撒,面向现代化了撒。哪个简单?
总结
如果说要速度的话,我选择用缓冲流的 5MB 和 10MB 大小;如果说要方便的话,我会用通道的方法去做。
后言
首篇博客,修改过。写得不好,虚心求教各位大神!!
非常感谢您的观看!您的收获就是我的最大动力!!再次感谢!!!