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([email protected])
 *
 */
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看看。

原文地址:https://www.cnblogs.com/ljgeng/p/9465446.html

时间: 2024-10-31 00:54:29

Java nio 的文件处理的相关文章

Java IO和Java NIO在文件拷贝上的性能差异分析 (转)

1.       在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取 到用户空间的缓冲区.参看read(byte b[])方法的源码,可知,它会在内部再调用readBytes(b, 0, b.length)方法,而且readBytes(b, 0, b.length)方法是一个native方法(即本地方法),最终通过这个本地方法来发起一次系统调用,即调用系统内核的read()

使用Java NIO监视文件

通过优锐课的java小课中学习到了很多重点,一起来看看了解有关使用Java NIO观看文件的更多信息. 该??java.nio.file??包提供了一个文件更改通知API,被称为??手表服务API.它使我们能够在监视服务中注册文件夹.注册时,我们告诉服务我们感兴趣的事件类型是:文件创建,文件修改或文件删除. 当服务检测到感兴趣的事件时,会将其转发到注册的进程并根据需要进行处理.基本上是这样的: 1.第一步是WatchService?使用类的newWatchService()?方法创建一个新??的

Java IO和Java NIO在文件拷贝上的性能差异分析

1.  在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区.参看read(byte b[])方法的源码,可知,它会在内部再调用readBytes(b, 0, b.length)方法,而且readBytes(b, 0, b.length)方法是一个native方法(即本地方法),最终通过这个本地方法来发起一次系统调用,即调用系统内核的read()方法,内核从

Java NIO.2 —— 文件或目录删除操作

文件删除 删除单个文件的操作很简单,如果要删除一个目录树的话,需要实现FileVisitor 接口然后递归地调用delete() 或deleteIfExists()方法.在看代码之前,需要注意一下问题. 在删除一个目录之前,要先删除里面的文件. visitFile()方式是执行删除每个文件最好的方式. 因为只有在目录为空的情况下才可以删除,所以建议在postVisitDirectory()方法中删除目录操作. 如果文件不允许访问,你可以根据自己的决定使用visitFileFailed()方法来返

Java NIO.2 —— 文件或目录拷贝操作

拷贝整个文件树是可以递归每个目录和文件调用 Files.copy()方法.在使用的时候有一下注意事项. 在往目录拷贝文件之前,首先要保证目录已经存在.拷贝源目录(不论是否为空)都会生成目标目录.整个任务一定在preVisitDirectory()方法中执行. visitFile()方式用来拷贝每个文件最佳的方法. 在你拷贝目录或文件时,你需要决定是否要使用REPLACE_EXISTING 和COPY_ATTRIBUTES 选项. 如果你想保存要拷贝的目录的属性,你需要在postVisitDire

Java NIO 读取文件、写入文件、读取写入混合

前言 Java NIO(new/inputstream outputstream)使用通道.缓冲来操作流,所以要深刻理解这些概念,尤其是,缓冲中的数据结构(当前位置(position).限制(limit).容量(capacity)),这些知识点要通过写程序慢慢体会. NIO vs  传统IO NIO是面向缓冲.通道的:传统IO面向流 通道是双向的既可以写.也可以读:传统IO只能是单向的 NIO可以设置为异步:传统IO只能是阻塞,同步的 缓冲区结构图 NIO是面向缓冲区的,缓冲区可以理解为一块内存

Java NIO 实现文件复制

/* *int bytesRead=inChannel.read(buf); * 这句话是从文件流中读取一个buf内容,返回读取的大小, * 如果是读取到文件尾部的时候,返回的是-1 * * 注意FileChannel.write()是在while循环中调用的. * 因为无法保证write()方法一次能向FileChannel写入多少字节, * 因此需要重复调用write()方法, * 直到Buffer中已经没有尚未写入通道的字节. * */ @Testpublic void test1() {

java nio实现文件复制

public class TransferTo { public static void main(String[] args) throws Exception { FileChannel in = new FileInputStream("src/demo20/data.txt").getChannel(), out = new FileOutputStream("src/demo20/data2.txt").getChannel(); in.transferT

转:Java NIO(3)

要想讲清楚nio的原理和它的优点得先清楚Java应用程序的文件读写原理和虚拟内存的原理.Java文件读取原理可参见如下图: 当应用程序需要读取文件的时候,内核首先通过DMA技术将文件内容从磁盘读入内核中的buffer,然后Java应用进程再从内核的buffer将数据读取到应用程序的buffer. 为了提升I/O效率和处理能力,操作系统采用虚拟内存的机制.虚拟内存也就是我们常说的交换内存,它实际上是硬盘上的文件,虚拟内存有两个作用: 1. 不同的虚拟内存可以映射到相同的物理内存,根据这个原理,可以