zoukankan      html  css  js  c++  java
  • Java nio 的文件处理

    一、创建一个大文件

    下载文件时往往会创建一个指定大小的空文件

    package com.lazy.nio;
    
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.nio.file.StandardOpenOption;
    
    /**
     * 创建大文件  
     * @author Lazy Gene
     *
     */
    public class FileCreator {
    	
    	public static void main(String[] args) {
    		FileCreator creator = new FileCreator();
    		creator.createBigEmptyFile();
    	}
    	
    	void createBigEmptyFile(){
    		
    		Path filePath = Paths.get("from/test.tmp");
              // 这段代码实际上可以在FIleChannel 调用open方式时指定OpenOption 为Create_NEW try { if (!Files.exists(filePath)) { Files.createFile(filePath); } } catch (IOException e1) { e1.printStackTrace(); } //写一个字节 ByteBuffer buffer = ByteBuffer.allocate(1); try (FileChannel fileChannel = FileChannel.open(filePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { fileChannel.position(2L << 32 - 1); //移动位置, 生成一个4G的空文件 fileChannel.write(buffer); } catch (IOException e) { e.printStackTrace(); } } }

      

    二、文件转移

    NIO 提供transferTo tansferFrom, 和传统的文件访问方式相比减少了数据从内核到用户空间的复制,数据直接在内核移动,在Linux系统中使用sendfile系统调用

    这里分别通过FileChannel.transferFrom 和Files.copy以及普通的io调用实现文件的复制

    package com.lazy.nio;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.nio.channels.FileChannel;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.nio.file.StandardOpenOption;
    
    /**
     * 文件转移
     * 
     * @author Lazy Gene(ljgeng@iflytek.com)
     *
     */
    public class FileTransfer {
    	Path fromPath = Paths.get("from/test.tmp");
    	Path toPath = Paths.get("to/test.tmp");
    
    	public static void main(String[] args) throws IOException {
    		FileTransfer transfer = new FileTransfer();
    		long start = System.currentTimeMillis();
    		transfer.transferFrom();
    		long end1 = System.currentTimeMillis();
    		System.out.println("transferFrom: "+ (end1 - start));
    		Files.deleteIfExists(transfer.toPath);
    		
    		transfer.copy();
    		long end2 = System.currentTimeMillis();
    		System.out.println("Files copy: "+(end2 - end1));
    		Files.deleteIfExists(transfer.toPath);
    		transfer.ioCopy();
    		long end3 = System.currentTimeMillis();
    		System.out.println("original io: "+(end3 - end2));
    	}
    
    	void transferFrom() {
    		try (FileChannel channelFrom = FileChannel.open(fromPath, StandardOpenOption.READ);
    				FileChannel channelTo = FileChannel.open(toPath, StandardOpenOption.CREATE_NEW,
    						StandardOpenOption.WRITE);) {
    			channelTo.transferFrom(channelFrom, 0L, channelFrom.size());
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    
    	void copy() {
    		try {
    			Files.copy(fromPath, toPath);
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    
    	void ioCopy() {
    		try (InputStream is = new FileInputStream(fromPath.toFile());
    				OutputStream os = new FileOutputStream(toPath.toFile());) {
    			byte[] buffer = new byte[4096];
    			int byteread = 0;
    			while ((byteread = is.read(buffer)) != -1) {
    				os.write(buffer, 0, byteread);
    			}
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e1) {
    			e1.printStackTrace();
    		}
    	}
    }
    

      

    执行结果

    transferFrom: 32726
    Files copy: 33002
    original io: 112975

    将ioCopy方法中每次读取字节数调大十倍和百倍后结果

    original io: 54743 (十倍)

    original io: 93110 (百倍)

    三、FileChannel.map 的使用

    FileChannel.map 将文件按照一定大小块映射到内存区域,程序方式内存区域操作文件,适合大文件只读操作,如大文件的md5校验。

    package com.lazy.nio;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.channels.FileChannel;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    /**
     * 使用介绍
     * @author Lazy Gene
     *
     */
    public class FileChannelMap {
    	
    	public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
    		// 计算test.tmp 的md5值,将文件分成多块计算
    		Path path = Paths.get("from/test.tmp");
    		File file = path.toFile();
    		RandomAccessFile accessFile = new RandomAccessFile(file, "r");
    		MessageDigest MD5 = MessageDigest.getInstance("MD5");
    		long start = System.currentTimeMillis();
    		long eachSize = 1024 * 1024L; 
    		long length = file.length();
    		int count = 1 + (int) (length / eachSize); //分块数量 
    		long remaining = length; // 剩下的大小
    		for (int i=0;i<count;i++) {
    			MD5.update(accessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, i * eachSize, Math.min(remaining, eachSize)));
    			remaining -= eachSize;
    		}
    		accessFile.close();
    		long end = System.currentTimeMillis();
    		System.out.println("用时:"+(end - start));
    		System.out.println("md5: "+ new String(Hex.encodeHex(MD5.digest())));
    		
    		//方法二
    		MessageDigest new_MD5 = MessageDigest.getInstance("MD5");
    		FileInputStream in=new FileInputStream(file);
    		byte[] buffer=new byte[65536];
    		int rv=0;
    		while((rv=in.read(buffer))>0) {
    			new_MD5.update(buffer,0,rv);
    		}
    		long end1 = System.currentTimeMillis();
    		in.close();
    		System.out.println("用时:"+(end1 - end));
    		System.out.println("io md5: "+ new String(Hex.encodeHex(new_MD5.digest())));
    	}
    }  

    对于4g文件运行结果:

    用时:15172
    md5: f18c798ff5d450dfe4d3acdc12b621ff
    用时:15811
    io md5: f18c798ff5d450dfe4d3acdc12b621ff

    差别不大呀,将文件调到16g,运行结果

    用时:65046
    md5: 0eb76b1bf69255feec7bdf4a3b5e2805
    用时:62697
    io md5: 0eb76b1bf69255feec7bdf4a3b5e2805

    这里只所以差别不大,估计是应用map操作和操作系统的底层io实现相关,下次换成linux看看。

  • 相关阅读:
    [Effective C++ --009]确定对象被使用前已先被初始化
    [Effective C++ --008]别让异常逃离析构函数
    [Effective C++ --007]为多态基类声明virtual析构函数
    [Effective C++ --006]若不能使用编译器自动生成的函数,就该明确拒绝
    [Effective C++ --005]了解C++默默编写并调用哪些函数
    [000]socket通信--server和client实现的简单例子
    [014]模板-模板实参推导
    [013]模板-非类型模板参数
    [012]泛型--lambda表达式捕获
    U3d keyCode值对应的按键
  • 原文地址:https://www.cnblogs.com/ljgeng/p/9465446.html
Copyright © 2011-2022 走看看