1 概述
- java.io.RandomAccessFile
- RandomAccessFile 用于在文件的任意位置读写数据,并且不会消耗太多的内存。
- RandomAccessFile 虽然属于
java.io
下的类,但它不是 InputStream 或者 OutputStream 的子类;它也不同于 FileInputStream 和 FileOutputStream。 FileInputStream 只能对文件进行读操作,而 FileOutputStream 只能对文件进行写操作。 - RandomAccessFile 与输入流和输出流不同之处就是 RandomAccessFile 可以访问文件的任意地方同时支持文件的读和写,并且它通过 seek 方法实现在文件的任意位置读写访问。
- RandomAccessFile 包含 InputStream 的三个 read 方法,也包含 OutputStream 的三个 write 方法。同时 RandomAccessFile 还包含一系列的 readXxx 和 writeXxx 方法完成输入输出。
2 关键点
- 通过 seek 方法设置开始随机读写文件的位置,以字节为单位。
- 通过 length 方法返回目标文件的长度,以字节为单位。
3 构造函数
RandomAccessFile(File file, String mode)
:创建随机访问文件流,以从 File 参数指定的文件中读取,并可选择写入文件。RandomAccessFile(String name, String mode)
: 创建随机访问文件流,以从中指定的完整文件路径和名称读取,并可选择写入文件。
其中构造函数中 mode 参数传值介绍:
r
代表以只读方式打开指定文件。rw
以读写方式打开指定文件。rws
读写方式打开,并对内容或元数据都同步写入底层存储设备。rwd
读写方式打开,对文件内容的更新同步更新至底层存储设备。
4 具体使用
4.1 每次固定从文件中读取指定数量的字节,并通过 Consumer 接口对象进行处理
- 封装功能如下
1 | /** |
- 测试如下
1 |
|
- 执行结果如下
1 | UTF-8 |
- 其中 E:\test.txt 的文件内容如下
1 | abeezzee |
4.2 跳过指定的字节数后再读取指定字节的数据
- 封装功能如下
1 | /** |
- 测试如下
1 |
|
- 执行结果如下
1 | UTF-8 |
- 其中 E:\test.txt 的文件内容如下
1 | abeezzee |
4.3 跳过指定的字节数后再 写入 指定字节的数据,最后返回被替换的字节数
- 封装功能如下
1 | /** |
- 测试如下
1 |
|
- 执行结果如下
1 | UTF-8 |
- 其中 E:\test.txt 的文件内容前后变化如下
1 | abeezzee |
1 | abyinzee |
4.4 向文件追加指定字节数据
- 封装功能如下
1 | /** |
- 测试如下
1 |
|
- 执行结果如下
1 | 追加数据前 data=abyinzeeabc |
5 方法
常用的方法如下:
void close()
: 关闭此随机访问文件流并释放与该流关联的所有系统资源。FileChannel getChannel()
: 返回与此文件关联的唯一 FileChannel 对象。FileDescriptor getFD()
: 返回与此流关联的不透明文件描述符对象。long getFilePointer()
: 返回此文件中的当前偏移量。long length()
: 返回此文件的长度。int read()
: 从此文件中读取一个数据字节。int read(byte[] b)
: 将最多 b.length 个数据字节从此文件读入 byte 数组。int read(byte[] b, int off, int len)
: 将最多 len 个数据字节从此文件读入 byte 数组。boolean readBoolean()
: 从此文件读取一个 boolean。byte readByte()
: 从此文件读取一个有符号的八位值。char readChar()
: 从此文件读取一个字符。double readDouble()
: 从此文件读取一个 double。float readFloat()
: 从此文件读取一个 float。void readFully(byte[] b)
: 将 b.length 个字节从此文件读入 byte 数组,并从当前文件指针开始。void readFully(byte[] b, int off, int len)
: 将正好 len 个字节从此文件读入 byte 数组,并从当前文件指针开始。int readInt()
: 从此文件读取一个有符号的 32 位整数。String readLine()
: 从此文件读取文本的下一行。long readLong()
: 从此文件读取一个有符号的 64 位整数。short readShort()
: 从此文件读取一个有符号的 16 位数。int readUnsignedByte()
: 从此文件读取一个无符号的八位数。int readUnsignedShort()
: 从此文件读取一个无符号的 16 位数。String readUTF()
: 从此文件读取一个字符串。void seek(long pos)
: 设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。void setLength(long newLength)
: 设置此文件的长度。int skipBytes(int n)
: 尝试跳过输入的 n 个字节以丢弃跳过的字节。void write(byte[] b)
: 将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。void write(byte[] b, int off, int len)
: 将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 off 处开始。void write(int b)
: 向此文件写入指定的字节。void writeBoolean(boolean v)
: 按单字节值将 boolean 写入该文件。void writeByte(int v)
: 按单字节值将 byte 写入该文件。void writeBytes(String s)
: 按字节序列将该字符串写入该文件。void writeChar(int v)
: 按双字节值将 char 写入该文件,先写高字节。void writeChars(String s)
: 按字符序列将一个字符串写入该文件。void writeDouble(double v)
: 使用 Double 类中的 doubleToLongBits 方法将双精度参数转换为一个 long,然后按八字节数量将该 long 值写入该文件,先定高字节。void writeFloat(float v)
: 使用 Float 类中的 floatToIntBits 方法将浮点参数转换为一个 int,然后按四字节数量将该 int 值写入该文件,先写高字节。void writeInt(int v)
: 按四个字节将 int 写入该文件,先写高字节。void writeLong(long v)
: 按八个字节将 long 写入该文件,先写高字节。void writeShort(int v)
: 按两个字节将 short 写入该文件,先写高字节。void writeUTF(String str)
: 使用 modified UTF-8 编码以与机器无关的方式将一个字符串写入该文件。
6 使用场景
- 断点续传,记录已经下载的字节数,继续下载的时候,跳过已经下载的字节,继续进行下载。
- 向大文件(比如 100G 的文件)任意位置读取或者插入,修改指定内容。
6.1 断点续传
- 手机端向服务器端下载文件,初始请求关键字段:
手机标识,文件名称,字节偏移(seek 方法的参数 = 0)
- 突然网络中断,手机端统计共下载了 25%,500 个字节,还剩下 1500 个字节
- 手机端再次向服务器发送下载请求的时候,需要请求的关键字段:
手机标识,文件名称,字节偏移(seek 方法的参数 = 500)
6.2 大文件读写
- 将数十亿商品详细信息存储在一个文件中,共计 10 G,然后在一个 hashMap 中存储了每个商品的 id 以及对应商品详细信息在文件中的偏移(offset)。
- 这样根据 id 找到 偏移(offset),就可以通过 seek(offset) 直接获取商品的详细信息。