有如下情况下可以用到内存文件映射技术解决问题:
1.不要复制文件中所有的数据,只需要修改文件中局部的数据。
2.并行/分段处理大文件。
如下代码示使用javaNIO局部修改文件中指定位置的部分数据:
/** * 修改文件中的某一部分的数据测试:将字定位置的字母改为大写 * @param fName :要修改的文件名字 * @param start:起始字节 * @param len:要修改多少个字节 * @return :是否修改成功 * @throws Exception:文件读写中可能出的错 * @author javaFound */
public static boolean changeFile(String fName,int start,int len) throws Exception{ //创建一个随机读写文件对象
java.io.RandomAccessFile raf=new java.io.RandomAccessFile(fName,"rw"); long totalLen=raf.length(); System.out.println("文件总长字节是: "+totalLen); //打开一个文件通道
java.nio.channels.FileChannel channel=raf.getChannel(); //映射文件中的某一部分数据以读写模式到内存中
java.nio.MappedByteBuffer buffer= channel.map(FileChannel.MapMode.READ_WRITE, start, len); //示例修改字节
for(int i=0;i<len;i++){ byte src= buffer.get(i); buffer.put(i,(byte)(src-31));//修改Buffer中映射的字节的值
System.out.println("被改为大写的原始字节是:"+src); } buffer.force();//强制输出,在buffer中的改动生效到文件
buffer.clear(); channel.close(); raf.close(); return true; } //测试主方法
public static void main(String[] args) throws Exception{ changeFile("BigFileRW.java",3,5); System.out.println(" change OK... "); }
内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件。有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问。这种解决办法能大大简化修改文件的代码。
fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中。注意,你必须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大;也就是说,它还可以映射一个大 文件的某个小片断。
MappedByteBuffer 是ByteBuffer的子类,因此它具备了ByteBuffer的所有方法,但新添了force()将缓冲区的内容强制刷新到存储设备中去、 load()将存储设备中的数据加载到内存中、isLoaded()位置内存中的数据是否与存储设置上同步。这里只简单地演示了一下put()和 get()方法,除此之外,你还可以使用asCharBuffer( )之类的方法得到相应基本类型数据的缓冲视图后,可以方便的读写基本类型数据。
import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class LargeMappedFiles { static int length = 0x8000000; // 128 Mb
public static void main(String[] args) throws Exception { // 为了以可读可写的方式打开文件,这里使用RandomAccessFile来创建文件。
FileChannel fc = new RandomAccessFile("test.dat", "rw").getChannel(); //注意,文件通道的可读可写要建立在文件流本身可读写的基础之上
MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, length); //写128M的内容
for (int i = 0; i < length; i++) { out.put((byte) 'x'); } System.out.println("Finished writing"); //读取文件中间6个字节内容
for (int i = length / 2; i < length / 2 + 6; i++) { System.out.print((char) out.get(i)); } fc.close(); } }
尽管映射写似乎要用到FileOutputStream,但是映射文件中的所有输出 必须使用RandomAccessFile,但如果只需要读时可以使用FileInputStream,写映射文件时一定要使用随机访问文件,可能写时要读的原因吧。
该程序创建了一个128Mb的文件,如果一次性读到内存可能导致内存溢出,但 这里访问好像只是一瞬间的事,这是因为,真正调入内存的只是其中的一小部分,其余部分则被放在交换文件上。这样你就可以很方便地修改超大型的文件了(最大 可以到2 GB)。注意,Java是调用操作系统的"文件映射机制"来提升性能的。