bson文件的切分

描述

最近遇到问题需要将较大的bson文件(MongoDB导出的二进制json文件)按文档(记录)进行切分,网上这方面的资料实在太少,弄了一天多终于达到了基本要求(还不知道有没有BUG)

代码

package splitbson;

import java.io.*;
import java.util.Scanner;

/**
 * 每条文档的前四个字节表示该文档的字节数,因此只需要读取4个字节数,计算该文档大小。然后用字节流读取即可。
 */
public class SplitBsonUtils {
    // 输入流
    private static BufferedInputStream bis;
    // 输出结果文件的命名编号
    private static int fileNameCount = 1;
    // 带缓冲区的输出流
    private static BufferedOutputStream bos;
    // 记录当前文件已写文档(记录)数
    private static int documentCount = 0;

    /**
     * 切分bson文件
     *
     * @param sourceFilePath  源bson文件
     * @param fileDocumentNum 每个文件的文档数
     * @param targetFilePath  目标文件目录
     */
    public static void splitBson(String sourceFilePath, int fileDocumentNum, String targetFilePath) {
        if (fileDocumentNum < 0) fileDocumentNum = 100;
        try {
            // 构建输入流
            bis = new BufferedInputStream(new FileInputStream(sourceFilePath));
            File dir = new File(targetFilePath);
            if (!dir.exists()) {
                dir.mkdir();
            }
            // 构建可缓冲的输出流
            bos = new BufferedOutputStream(new FileOutputStream(targetFilePath + "/file" + fileNameCount++ + ".bson", true));
            // 获取下一条记录的字节数
            int documentSize = getSize(sourceFilePath);
            // 减4是因为getSize方法已经读写了四个字节
            byte[] arr = new byte[documentSize - 4];
            // 开始读源bson文件
            while (bis.read(arr) != -1) {
                // 写入到新的文件
                bos.write(arr);
                documentCount++;
                // 判断当前文件记录数是否达到自定义文档数
                if (documentCount == fileDocumentNum) {
                    // 创建新的输出流
                    bos = new BufferedOutputStream(new FileOutputStream(targetFilePath + "/file" + fileNameCount++ + ".bson", true));
                    documentCount = 0;
                }
                documentSize = getSize(sourceFilePath);
                // 表示已经到了文件结尾
                if (documentSize == -1) break;
                arr = new byte[documentSize - 4];
            }
            bos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bis.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取源bson文件正被读取文档的字节数
     *
     * @param filePath
     * @return
     */
    public static int getSize(String filePath) {
        byte[] arr = new byte[4];
        int size = 0;
        try {
            bis.read(arr);
            size = byte2DecStr(arr);
            if (size - 4 < 0) return -1;
            bos.write(arr);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return size;
    }

    /**
     * byte数组转换成十进制字符串
     *
     * @param b
     * @return
     */
    public static int byte2DecStr(byte[] b) {
        String stmp = "";
        StringBuilder sb = new StringBuilder("");
        for (int n = b.length - 1; n >= 0; n--) {
            stmp = Integer.toHexString(b[n] & 0xFF);
            sb.append((stmp.length() == 1) ? "0" + stmp : stmp);
        }
        return Integer.parseInt(sb.toString(), 16);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请传入三个参数进行bson文件的切分:");
        System.out.println("1. 原bson文件绝对路径(无需引号),如:E:/ld_FamilySearch_detail.bson");
        String sourceFilePath = scanner.nextLine();
        System.out.println("2. 切分后每个文件所要存储的文档(记录)条数,如:100");
        int fileDocumentNum = scanner.nextInt();
        scanner.nextLine();
        System.out.println("3. 切分后文件存储的目录(无需引号),如:E:/result");
        String targetFilePath = scanner.nextLine();
        System.out.println(sourceFilePath + fileDocumentNum + targetFilePath);
        System.out.println("正在进行切分...");
        long start = System.currentTimeMillis();
        splitBson(sourceFilePath, fileDocumentNum, targetFilePath);
        long end = System.currentTimeMillis();
        System.out.println("切分完成!共耗时:" + (end - start) + "毫秒");
    }
}

posted @ 2017-11-08 11:57  zyoung  阅读(803)  评论(0编辑  收藏  举报