FileChannel、ByteBuffer对文件操作过程对比

#内存映射文件#

文章背景:对文件进行MD5操作时获取到文件的途径多样及处理方式略有不同,博主经过对比希望得出较为合理的方式。

FileChannel:用于读取、写入、映射和操作文件的通道。 (以下解释源自JavaAPI文档)

文件通道在其文件中有一个当前 position,可对其进行查询和修改。该文件本身包含一个可读写的长度可变的字节序列,并且可以查询该文件的当前大小。写入的字节超出文件的当前大小时,则增加文件的大小;截取 该文件时,则减小文件的大小。文件可能还有某个相关联的元数据,如访问权限、内容类型和最后的修改时间;此类未定义访问元数据的方法。

除了字节通道中常见的读取、写入和关闭操作外,此类还定义了下列特定于文件的操作:

以不影响通道当前位置的方式,对文件中绝对位置的字节进行读取或写入。

将文件中的某个区域直接映射到内存中;对于较大的文件,这通常比调用普通的 read 或 write 方法更为高效。

强制对底层存储设备进行文件的更新,确保在系统崩溃时不丢失数据。

以一种可被很多操作系统优化为直接向文件系统缓存发送或从中读取的高速传输方法,将字节从文件传输到某个其他通道中,反之亦然。

可以锁定某个文件区域,以阻止其他程序对其进行访问。

--------------------------------------------------------------------------------------------

FileChannel 类的map方法可以从该通道中获得一个ByteBuffer (指定映射的文件区域及映射模式)

FileChannel.MapMode.READ_ONLY    产生只读的缓冲区 (下文例子过程中得以体现)

FileChannel.MapMode.READ_WRITE   产生可写的缓冲区 (可影响文件内容)

FileChannel.MapMode.PRIVATE            产生可写的缓冲区 但不会影响传至文件


=====================================================

ByteBuffer:字节缓冲区。  (本文目的不在于详细解释类 故不再介绍方法)

此类针对字节缓冲区定义了以下六类操作:

读写单个字节的绝对和相对 get 和 put 方法;

将此缓冲区的连续字节序列传输到数组中的相对批量 get 方法;

将 byte 数组或其他字节缓冲区中的连续字节序列传输到此缓冲区的相对批量 put 方法;

读写其他基本类型值,并按照特定的字节顺序在字节序列之间转换这些值的绝对和相对 get 和 put 方法;

创建视图缓冲区 的方法,这些方法允许将字节缓冲区视为包含其他基本类型值的缓冲区;

对字节缓冲区进行 compacting、duplicating 和 slicing 的方法。

字节缓冲区可以通过 allocate 方法创建,此方法为该缓冲区的内容分配空间,或通过 wrapping 方法将现有的 byte 数组包装到缓冲区中来创建。

---------------------------------------------------------------------------------------------

ByteBuffer 的创建需要以allocate来指定分配的缓冲区大小 

=====================================================

实验开始:

文件素材:D:\JAVA3D.zip   21.7MB  D:\MyRecord.txt 16bit

import java.io.File; 
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/*消息摘要算法相关内容
 * 
 * */
public class MessageDigestTest {

	public static void Test2() throws NoSuchAlgorithmException{
		File myFile=null;
		FileChannel fc=null;
		try {
			 myFile =new File("D:\\JAVA3D.zip");
			 fc = new FileInputStream(myFile).getChannel();
		ByteBuffer byteBuffer=ByteBuffer.allocate((int)myFile.length());
		try {
			fc.read(byteBuffer);
			
			MessageDigest sha=MessageDigest.getInstance("MD5");
				String stt=new BigInteger(1,sha.digest(byteBuffer.array())).toString(16);
				System.out.println(stt);
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}finally {
			if(fc!=null){
				try {
					fc.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	public static void Test3() throws NoSuchAlgorithmException{
		File myFile=null;
		FileChannel fc=null;
		try {
			 myFile =new File("D:\\JAVA3D.zip");
			 fc = new FileInputStream(myFile).getChannel();
		ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
		int len;
		try {
			MessageDigest sha=MessageDigest.getInstance("MD5");
			while((len=fc.read(byteBuffer))>0)
			{ sha.update(byteBuffer.array(), 0, len);
			  byteBuffer.flip();
			  byteBuffer.clear();
			}
                 String stt=new BigInteger(1,sha.digest()).toString(16);
				System.out.println(stt);
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}finally {
			if(fc!=null){
				try {
					fc.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	public static void Test4() throws NoSuchAlgorithmException{
		FileChannel fc=null;
		try {
			 fc=new FileInputStream("D:\\JAVA3D.zip").getChannel();
			 try {
				int length=(int)fc.size();
				 MappedByteBuffer mbbf=fc.map(FileChannel.MapMode.READ_ONLY, 0, length);
				 MessageDigest sha=MessageDigest.getInstance("MD5");
				 sha.update(mbbf);
				 String stt=new BigInteger(1,sha.digest()).toString(16);
				 System.out.println(stt);
				 
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}finally {
			if(fc!=null){
				try {
					fc.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	public static void main(String[] args) throws NoSuchAlgorithmException {
		long begin=System.currentTimeMillis();
		Test2();
		long end=System.currentTimeMillis();
		System.out.println((end-begin)+" ms");
	}

}

输出结果如下: (由于程序代码执行牵扯内存占用问题,每次执行数据间隔会有些许偏差)
D:\JAVA3D.zip  组 

1.Test3()中 指定ByteBuffer大小为1024  Test2()中为文件大小
5417f2fe5eafc7b531a91ed391dbec89
423 ms
5417f2fe5eafc7b531a91ed391dbec89
539 ms
5417f2fe5eafc7b531a91ed391dbec89
372 ms
-------------------------------------分析:Test4<Test2<Test3   T3中利用循环读取,虽然缓冲内存小了但执行次数加大
2.Test3()中 指定ByteBuffer大小为5120  Test2()中为文件大小
5417f2fe5eafc7b531a91ed391dbec89
424 ms
5417f2fe5eafc7b531a91ed391dbec89
417 ms
5417f2fe5eafc7b531a91ed391dbec89
358 ms
-------------------------------------分析:Test4<Test3<Test2   T3中加大了缓冲空间,减小了循环次数,但终不如T4
D:\MyRecord.txt  组 
Test3()中 指定ByteBuffer大小为1024  Test2()中为文件大小
d063036c28b1dd869ffedb2e924b6f88
55 ms
d063036c28b1dd869ffedb2e924b6f88
57 ms
d063036c28b1dd869ffedb2e924b6f88
59 ms

-------------------------------------分析:由于文件大小仅为16字节,三者基本等同  

总结:使用内存映射进行读取文件,执行效果可观,有良好效益。

本例只是简单介绍两者的异同,若想提高效率,请继续阅读另外两篇,相信会有更多的收获(虽然后两篇以加密解密为主,但其中的执行方法的确是经过一些优化的,比直接简单读写要快)

“java证书加解密过程” : http://blog.csdn.net/only06/article/details/52090169

java非证书下加解密小解析”: http://blog.csdn.net/only06/article/details/52014693


展开阅读全文

没有更多推荐了,返回首页