1)简单了解“流”
在Java中,文件的输入和输出是通过流(Stream)来实现的一个流,必有它的起始端和目的端。对于流而言,我们不用关心数据是如何传输的,只需要向起始端输入数据,从目的端获取数据即可。
流按照处理数据的单位,可以分为字节流和字符流;字节流的处理单位是字节,通常用来处理二进制文件,例如视频、图片文件等。而字符流的处理单位是字符,因为Java采用Unicode编码,Java字符流处理的即为Unicode字符,所以在操作汉字、国际化等方面,字符流具有一定的优势。
2)有关文件流的传输
我们今天要说的文件操作就是基于字节流传输的,所有的字节流类都继承自InputStream 和 OutputStream 这两个抽象类,进行字节流传输的操作是基于rea()和write()方法完成的,即
FileInputStream:把一个文件作为输入源,从本地文件系统中读取数据字节,通过调用read()方法实现对文件的读取操作。
FileOutputStream:把读取到的数据字节,通过调用write()方法写入到目标文件中。
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file);
参数是根据文件的路径获得的具体文件file,举个简单例子来看一下文件的路径;
File f = new File(".");
System.out.println(f.getAbsolutePath());
上述操作可以显示出该文件的绝对路径,看结果:
J:\Java SE\我的Web学习\2020-04-01@文件操作\.
也就是说我们指定文件的路径时,可以基于上面的绝对路径开始,例如可以写成下面的形式;
FileInputStream fis = new FileInputStream("./bin/res/wuming.mp3");
3)利用字节流的输入输出完成文件的拷贝
基于上述的理解,下面给出一个音频文件拷贝的例子来进一步学习,目标是将本机目录下的J:\Java SE\我的Web学习\2020-04-01@文件操作./bin/res/wuming.mp3音频文件拷贝到bin目录下的tag文件中;
public class Test {
public static final int BUFFER_SIZE = 1 << 15;
public static void main(String[] args) {
File file = new File("./bin/res/wuming.mp3");
try {
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream("./bin/tag/wuming.mp3");
byte[] buffer = new byte[BUFFER_SIZE];
long startTime = System.currentTimeMillis();
int readLen = 0;
readLen = fis.read(buffer);
while(readLen != -1) {
fos.write(buffer,0,readLen);
readLen = fis.read(buffer);
}
fis.close();
fos.close();
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime + " ms");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
首先要清楚的一点是read()方法得到的结果是int类型的数值,那么,什么时候才算是将文件内容全部读取完呢,根据read()方法的返回结果来看,应该是当返回值为-1的时候,文件中的内容就全部读取完毕了。
从上述代码来看,while循环中的readLen = fis.read(buffer)最后执行完返回判断readLen是否等于-1,此时因为后面已经没有要读的内容了,所以while循环停止不再继续执行,但是readLen = fis.read(buffer)在判断之前已经多执行了一次,重复了最后一次读取的内容,会造成最终读取到的文件出现偏差,所以,这种方式实际上是存在缺陷的。
下面再给出一种改进的方法,修改部分如下:
int readLen = 0;
int len = 0;
int restLen = (int)file.length();
while(restLen > 0) {
len = restLen >= BUFFER_SIZE ? BUFFER_SIZE : restLen;
readLen = fis.read(buffer, 0, len);
fos.write(buffer, 0, readLen);
restLen -= readLen;
}
采用上述的方式,给一个剩余长度,目标读取长度,实际读取长度,根据每次实际读取的量来确定相对应写入的量,每次读多少就写多少,这样避免了会重复读取的情况,是目前我可以做到的比较好的处理方式。
注:read()和write()的三参方法的各个参数解释,以上述代码为例,参数buffer是每次读取(或接收)的字节段,第二个参数0是起始读取的长度,第三个参数len是终止读取的长度,后两个参数的含义就是每次读取的字节偏移量。
下面来看一下拷贝的结果:
(图1)
(图2)
图1是源文件,图二是目标拷贝出的文件,注意图片上方的文件路径。