Java文件分块读写,文件分片读写(大文件分块写入本地并合入)

867 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Java对大文件进行分片的实例:
import java.io.*;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

/**
 * 大文件分块写入本地并合入
 */
public class FilePart文件分块写入本地并合入 {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        // 调用文件分片方法
//        testFilePart();
        // 调用文件合分片方法
//        testFileMerge();
        // 调用文件快速合分片方法
        fastFileMerge();
        long end = System.currentTimeMillis();
        System.out.println("耗时=" + (end - start) + "ms");
    }

    /**
     * 大文件分片写入本地
     *
     * @throws IOException
     */
    private static void testFilePart() throws IOException {
        File sourceFile = new File("D:/TEST/testFile0.9G文件.zip");
        String chunkPath = "d:/testPart/";
        File chunkFolder = new File(chunkPath);
        // 如果该文件夹
        if (!chunkFolder.exists()) {
            chunkFolder.mkdirs();
        }
        // 设定文件分块大小,设定100M
        long chunkSize = 1024 * 1024 * 100;
        // 分块数量
        long chunkNum = (long) Math.ceil(sourceFile.length() * 1.0 / chunkSize);
        if(chunkNum<=0){
            chunkNum=1;
        }
        // 设置缓冲区
        byte[] bytes = new byte[1024];  // 和下面方式创建byte[]效率基本一样
//        byte[] bytes = new byte[(int) sourceFile.length()];
        BufferedInputStream fis = new BufferedInputStream(new FileInputStream(sourceFile));
        // 开始分块
        for (int i = 1; i <= chunkNum; i++) {
            // 创建分块文件
            File file = new File(chunkPath + i);
            if(file.createNewFile()){
                // 向分块文件中写数据
                BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
                int len = -1;
                while ((len = fis.read(bytes)) != -1) {
                    fos.write(bytes, 0, len); // 写入数据
                    if(file.length()>chunkSize){
                        break;
                    }
                }
                fos.close();
            }
        }
        fis.close();
    }

    /**
     * 把本地的文件片合并到一起
     *
     * @throws IOException
     */
    private static void testFileMerge() throws IOException {
        // 需要合并的文件所在的文件夹
        File chunkFolder = new File("d:/testPart/");
        // 合并后的文件
        File mergeFile = new File("d:/testPartMerge.zip");
        // 如果该文件夹
        if (mergeFile.exists()) {
            mergeFile.delete();
        }
        // 创建合并后的文件
        mergeFile.createNewFile();
        BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(mergeFile));
        // 设置缓冲区
        byte[] bytes = new byte[1024];  // 和下面方式创建byte[]效率基本一样
//        byte[] bytes = new byte[(int) sourceFile.length()];
        // 获取分块列表
        File[] fileArray = chunkFolder.listFiles();
        // 把文件转成集合并排序
        ArrayList<File> fileList = new ArrayList<>(Arrays.asList(fileArray));
        // 从小到大进行排序
        Collections.sort(fileList, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if(Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())){
                    return -1;
                }
                return 1;
            }
        });
        // 合并文件
        for (File chunkFile : fileList) {
            BufferedInputStream fis = new BufferedInputStream(new FileInputStream(chunkFile));
            int len = -1;
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len); // 写入数据
            }
            fis.close();
        }
        fos.close();
    }

    /**
     * 快速把本地的文件片合并到一起FileChannel
     *
     * @throws IOException
     */
    private static void fastFileMerge() throws IOException {
        // 需要合并的文件所在的文件夹
        File chunkFolder = new File("d:/testPart/");
        // 合并后的文件
        File mergeFile = new File("d:/testPartMerge.zip");
        // 如果该文件夹
        if (mergeFile.exists()) {
            mergeFile.delete();
        }
        // 创建合并后的文件
        mergeFile.createNewFile();
        // 获取分块列表
        File[] fileArray = chunkFolder.listFiles();
        // 把文件转成集合并排序
        ArrayList<File> fileList = new ArrayList<>(Arrays.asList(fileArray));
        // 从小到大进行排序
        Collections.sort(fileList, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if(Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())){
                    return -1;
                }
                return 1;
            }
        });
        // 合并文件
        try(FileChannel fosChannel = new FileOutputStream(mergeFile).getChannel()) {
            for (File chunkFile : fileList) {
                try(FileChannel fisChannel =  new FileInputStream(chunkFile).getChannel()) {
                    fisChannel.transferTo(0, fisChannel.size(), fosChannel);
                }catch (IOException ex){
                    System.out.println("IOException" + ex);
                }
            }

        }catch (IOException ex){
            System.out.println("IOException" + ex);
        }
    }
}

经过运行测试得出结论:

用BufferedInputStream和BufferedOutputStream进行分片和分片合入(0.9G文件):
分片:耗时=6823ms
合并分片:耗时=2383ms
用FileInputStream和FileOutputStream进行分片和分片合入(0.9G文件):
分片:耗时=11688ms
合并分片:耗时=7821ms
用RandomAccessFile进行分片和分片合入(0.9G文件):
分片:耗时=11282ms
合并分片:耗时=12539ms
用FileChannel进行分片和分片合入(0.9G文件): 
合并分片(循环读写):耗时=802ms
总结:普通随机流速度最慢,如果不是要截取文件中的一部分内容,不推荐使用RandomAccessFile,FileChannel循环合并速度最快。