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)

java 文件流是指 java传文件流是穿什么_Java


(图2)

java 文件流是指 java传文件流是穿什么_java 文件流是指_02


图1是源文件,图二是目标拷贝出的文件,注意图片上方的文件路径。