Java nio 的文件處理
阿新 • • 發佈:2018-08-13
package 18C puts algo instance 用戶 getc 文件訪問 void
一、創建一個大文件
下載文件時往往會創建一個指定大小的空文件
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看看。
Java nio 的文件處理