Java实现大文件分割与合并

最近有一个需求,要将一个大文件分割为几个小文件,然后将小文件再合并还原成大文件。

需求很简单,实现起来也很简单。

  • 文件分割,就是读取大文件,然后按照指定的大小读取到缓冲区,然后将缓冲区写入小文件。
  • 文件合并,就是按照顺序读取小文件到缓冲区,所有小文件读取完成后,一次性将缓冲区写入大文件

话不多说,直接看代码:

/**
 * 将大文件切割为小文件
 *
 * @param inputFile  大文件
 * @param tmpPath    小文件临时目录
 * @param bufferSize 切割小文件大小
 */
public static void splitFile(String inputFile, String tmpPath, Integer bufferSize) {
    
    
    FileInputStream fis = null;
    FileOutputStream fos = null;
    try {
    
    
        // 原始大文件
        fis = new FileInputStream(inputFile);

        // 文件读取缓存
        byte[] buffer = new byte[bufferSize];
        int len = 0;

        // 切割后的文件计数(也是文件名)
        int fileNum = 0;

        // 大文件切割成小文件
        while ((len = fis.read(buffer)) != -1) {
    
    
            fos = new FileOutputStream(tmpPath + "/" + fileNum);
            fos.write(buffer, 0, len);
            fos.close();
            fileNum++;
        }
        System.out.println("分割文件" + inputFile + "完成,共生成" + fileNum + "个文件");
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        if (fis != null) {
    
    
            try {
    
    
                fis.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
        if (fos != null) {
    
    
            try {
    
    
                fos.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

/**
 * 合并切割小文件为大文件
 *
 * @param tmpPath    小文件临时目录
 * @param outputPath 输出路径
 * @param bufferSize 切割小文件大小
 */
public static void mergeFile(String tmpPath, String outputPath, Integer bufferSize) {
    
    
    FileInputStream fis = null;
    FileOutputStream fos = null;
    try {
    
    
        // 获取切割的小文件数目
        File tempFilePath = new File(tmpPath);
        File[] files = tempFilePath.listFiles();
        if (files == null) {
    
    
            System.out.println("No file.");
            return;
        }
        int fileNum = files.length;

        // 还原的大文件路径
        String outputFile = outputPath + "/" + generateFileName();
        fos = new FileOutputStream(outputFile);

        // 文件读取缓存
        byte[] buffer = new byte[bufferSize];
        int len = 0;

        // 还原所有切割小文件到一个大文件中
        for (int i = 0; i < fileNum; i++) {
    
    
            fis = new FileInputStream(tmpPath + "/" + i);
            len = fis.read(buffer);
            fos.write(buffer, 0, len);
        }
        System.out.println("合并目录文件:" + tmpPath + "完成,生成文件为:" + outputFile);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        if (fis != null) {
    
    
            try {
    
    
                fis.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
        if (fos != null) {
    
    
            try {
    
    
                fos.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

/**
 * 生成随机文件名
 *
 * @return 文件名
 */
public static String generateFileName() {
    
    
    String time = DateFormatUtils.format(new Date(), "yyyMMddHHmmss");
    return time + ".7z";
}

测试上述代码

String localInputFile = "F:/test/file/in/in.7z";
String localTmpPath = "F:/test/file/tmp";
String localOutputPath = "F:/test/file/out";
Integer localBufferSize = 1024 * 1024;
splitFile(localInputFile, localTmpPath, localBufferSize);
mergeFile(localTmpPath, localOutputPath, localBufferSize);

以上就是切割文件与合并文件的代码,逻辑很简单,是使用二进制方式读取的,所以打开的临时文件会是乱码。

如果要读取成字符串,就可以避免乱码的问题了。

那么如何读取成字符串呢?
我们可以将读取成的二进制流,再通过Base64编码,这样就可以变成字符串了。但是与此同时也带来一个问题,就是小文件大小会略有膨胀。

以下是实现代码:

/**
 * 将大文件切割为小文件(字符串)
 *
 * @param inputFile  要切割的大文件
 * @param tmpPath    小文件临时目录
 * @param bufferSize 切割大小(二进制读取大小)
 */
public static void splitFileByChar(String inputFile, String tmpPath, Integer bufferSize) {
    
    
    FileInputStream fis = null;
    FileWriter fw = null;
    try {
    
    
        // 原始大文件
        fis = new FileInputStream(inputFile);

        // 文件读取缓存
        byte[] buffer = new byte[bufferSize];

        // 切割后的文件计数(也是文件名)
        int fileNum = 0;

        // 大文件切割成小文件
        while ((fis.read(buffer)) != -1) {
    
    
            fw = new FileWriter(tmpPath + "/" + fileNum + ".txt");
            // base64将二进制流转为字符串
            String tmpStr = Base64.getEncoder().encodeToString(buffer);
            fw.write(tmpStr);
            fw.close();
            fileNum++;
        }
        System.out.println("分割文件" + inputFile + "完成,共生成" + fileNum + "个文件");
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        if (fis != null) {
    
    
            try {
    
    
                fis.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
        if (fw != null) {
    
    
            try {
    
    
                fw.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

/**
 * 合并小文件为大文件
 *
 * @param tmpPath    小文件临时目录
 * @param outputPath 大文件输出目录
 * @param bufferSize 切割大小(二进制读取大小)
 */
public static void mergeFileByChar(String tmpPath, String outputPath, Integer bufferSize) {
    
    
    FileReader fr = null;
    FileOutputStream fos = null;
    try {
    
    
        // 获取切割的小文件数目
        File tempFilePath = new File(tmpPath);
        File[] files = tempFilePath.listFiles();
        if (files == null || files.length <= 0) {
    
    
            System.out.println("No file.");
            return;
        }
        int fileNum = files.length;

        // 生成的大文件路径
        String outputFile = outputPath + "/" + generateFileName();
        fos = new FileOutputStream(outputFile);

        // 还原所有切割小文件到一个大文件中
        for (int i = 0; i < fileNum; i++) {
    
    
            fr = new FileReader(tmpPath + "/" + i + ".txt");

            // 读取出base64编码后的数据(*2减少读取次数,因为base64之后文件会略有膨胀)
            char[] buffer = new char[bufferSize * 2];
            int len;
            StringBuilder tmpStr = new StringBuilder();
            while ((len = fr.read(buffer)) != -1) {
    
    
                tmpStr.append(new String(buffer, 0, len));
            }

            // base64将字符转为二进制流
            byte[] tmpBuffer = Base64.getDecoder().decode(tmpStr.toString());
            fos.write(tmpBuffer, 0, tmpBuffer.length);
        }
        System.out.println("合并目录文件:" + tmpPath + "完成,生成文件为:" + outputFile);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        if (fr != null) {
    
    
            try {
    
    
                fr.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
        if (fos != null) {
    
    
            try {
    
    
                fos.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

/**
 * 生成随机文件名
 *
 * @return 文件名
 */
public static String generateFileName() {
    
    
    String time = DateFormatUtils.format(new Date(), "yyyMMddHHmmss");
    return time + ".7z";
}

测试上述代码

String localInputFile = "F:/test/file/in/in.7z";
String localTmpPath = "F:/test/file/tmp";
String localOutputPath = "F:/test/file/out";
Integer localBufferSize = 1024 * 1024;
splitFileByChar(localInputFile, localTmpPath, localBufferSize);
mergeFileByChar(localTmpPath, localOutputPath, localBufferSize);

打开临时目录,就能看到我们想要的字符串格式小文件了。不过每个小文件大小都超过了我们预设的1M大小,约为1366K。

两种方式分割、还原文件,记录一下。

猜你喜欢

转载自blog.csdn.net/somehow1002/article/details/106978884