基于java的文件分割器

转载请注明出处!
http://blog.csdn.net/hitliuxiaodong/article/details/52268818
前几天参加酷家乐的面试,两轮技术电话面,一轮电话hr面,然后hr通知我现场面,我以为现场应该是hr面,结果去了让写一个文件分割器。写了三个多小时,发现文件可以被成功分割,可是合并的时候一直出问题,然后面试好像就gg了(—–分割— 最新情況,給offer了 )。回来不甘心,重新写了一下,发现RandomAccessFile 的用法写错了。怪不得一直出现越界错误。(使用RandomAccessFile 写文件时需要指定文件大小)
题目是:实现一个文件分割合并器,可以对任意文件分割指定的n份到一个目录下,同时分割后的文件名称可能被修改,所以不能依靠文件名称进行合并。
所以我想的是 以二进制流的形式进行读取,然后分割为n份后,在每一份文件的头部添加文件信息,然后再写入每一块小文件,合并时就可以根据文件的头部信息进行校验合并。

首先是一个工具类ByteUtils.java ,可以进行int 转bytes 和 bytes转int

package com.xiaodongliu;

import java.nio.ByteBuffer;

/**
 * Created by lxd on 16-8-20.
 */
public class ByteUtils {
    public static void setIntBytes(byte[] dst, int offset, int value) {
        dst[offset] = (byte) (value & 0xff);// 最低位
        dst[offset + 1] = (byte) ((value >> 8) & 0xff);// 次低位
        dst[offset + 2] = (byte) ((value >> 16) & 0xff);// 次高位
        dst[offset + 3] = (byte) (value >>> 24);// 最高位,无符号右移。
    }

    public static int getIntFromBytes(byte[] src, int offset) {
        int targets = (src[offset] & 0xff) | ((src[offset + 1] << 8) & 0xff00) // | 表示安位或
                | ((src[offset + 2] << 24) >>> 8) | (src[offset + 3] << 24);
        return targets;
    }

}

然后是一个文件信息的类 FileBlockInfo.java

package com.xiaodongliu;

/**
 * Created by lxd on 16-8-20.
 */
public class FileBlockInfo {
    private String fileName;//文件名
    private int fileNameLength;//文件名长度
    private int offset;
    private int blockIndex;//第几块
    private int allBlockCount;//文件总分数
    private int blockSize;//该文件块长度
    private int allSize;//文件块总长度
    private byte[] data;//该块数据

      public FileBlockInfo(byte[] header) {
        fileNameLength = ByteUtils.getIntFromBytes(header, 0);
        offset = ByteUtils.getIntFromBytes(header, 4);
        blockIndex = ByteUtils.getIntFromBytes(header, 8);
        allBlockCount = ByteUtils.getIntFromBytes(header, 12);
        blockSize = ByteUtils.getIntFromBytes(header, 16);
        allSize = ByteUtils.getIntFromBytes(header, 20);
        System.out.println(toString());

    public byte[] getIntegrateData() {
        byte[] integeratedData = new byte[24 + fileNameLength + blockSize];
        ByteUtils.setIntBytes(integeratedData, 0, fileNameLength);
        ByteUtils.setIntBytes(integeratedData, 4, offset);
        ByteUtils.setIntBytes(integeratedData, 8, blockIndex);
        ByteUtils.setIntBytes(integeratedData, 12, allBlockCount);
        ByteUtils.setIntBytes(integeratedData, 16, blockSize);
        ByteUtils.setIntBytes(integeratedData, 20, allSize);
        System.arraycopy(fileName.getBytes(), 0, integeratedData, 24, fileNameLength);
        System.arraycopy(data, 0, integeratedData, 24 + fileNameLength, blockSize);
        return integeratedData;
    }
    @Override
    public String toString() {
        return "FileBlockInfo{" +
                "fileNameLength=" + fileNameLength +
                ", offset=" + offset +
                ", blockIndex=" + blockIndex +
                ", allBlockCount=" + allBlockCount +
                ", blockSize=" + blockSize +
                ", allSize=" + allSize +
                '}';
    }

    public byte[] getData() {
        return data;
    }

    public void setData(byte[] data) {
        this.data = data;
    }

    public FileBlockInfo() {
    }



    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public int getFileNameLength() {
        return fileNameLength;
    }

    public void setFileNameLength(int fileNameLength) {
        this.fileNameLength = fileNameLength;
    }

    public int getOffset() {
        return offset;
    }

    public void setOffset(int offset) {
        this.offset = offset;
    }

    public int getBlockIndex() {
        return blockIndex;
    }

    public void setBlockIndex(int blockIndex) {
        this.blockIndex = blockIndex;
    }

    public int getAllBlockCount() {
        return allBlockCount;
    }

    public void setAllBlockCount(int allBlockCount) {
        this.allBlockCount = allBlockCount;
    }

    public int getBlockSize() {
        return blockSize;
    }

    public void setBlockSize(int blockSize) {
        this.blockSize = blockSize;
    }

    public int getAllSize() {
        return allSize;
    }

    public void setAllSize(int allSize) {
        this.allSize = allSize;
    }


}

接下来就是实现的分割功能 Splitter.java

package com.xiaodongliu;

import java.io.*;

/**
 * Created by lxd on 16-8-20.
 */
public class Splitter {
    private String sourceFile;
    private int blockNum;
    private String dstPath;
    private String fileName;

    public Splitter(String sourceFile, int blockNum, String dstPath) {
        this.sourceFile = sourceFile;
        this.blockNum = blockNum;
        this.dstPath = dstPath;
        this.fileName = this.sourceFile.substring(this.sourceFile.lastIndexOf("/")+ 1);
    }

    public void StartSplit() {
        File file = new File(sourceFile);
        if (file.exists()) {
            int fileSize = (int) file.length();
            int pieceSize = (int) (fileSize / blockNum);
            if ((fileSize - pieceSize * blockNum) != 0) {
                pieceSize = (int) (fileSize / (blockNum - 1));
            }
            byte[] data = new byte[pieceSize];
            FileInputStream fileInputStream = null;
            File dstFold=new File(dstPath);
            if(!dstFold.exists()){
                dstFold.mkdirs();
            }
            try {
                fileInputStream = new FileInputStream(file);
                int realSize = 0;
                int offset = 0;
                int blockIndex = 0;
                while ((realSize = fileInputStream.read(data, 0, pieceSize)) != -1) {

                    FileBlockInfo fileBlockInfo = new FileBlockInfo();
                    fileBlockInfo.setFileName(fileName);
                    fileBlockInfo.setFileNameLength(fileName.getBytes().length);
                    fileBlockInfo.setOffset(offset);
                    fileBlockInfo.setBlockIndex(blockIndex);
                    fileBlockInfo.setAllBlockCount(blockNum);
                    fileBlockInfo.setBlockSize(realSize);
                    fileBlockInfo.setAllSize(fileSize);
                    fileBlockInfo.setData(data);
                    data = new byte[pieceSize];
                    blockIndex++;
                    offset += realSize;
                    new WriteAsync(dstPath + blockIndex, fileBlockInfo.getIntegrateData()).start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    class WriteAsync extends Thread {
        private String fileName;
        private byte[] data;

        public WriteAsync(String fileName, byte[] data) {
            this.fileName = fileName;
            this.data = data;
        }

        @Override
        public void run() {
            File file = new File(fileName);
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(file);
                fileOutputStream.write(data);
                fileOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("第" + fileName + "份写入完成");
        }
    }
}

以及实现合并功能的类Merger.java

package com.xiaodongliu;

import java.io.*;

/**
 * Created by lxd on 16-8-20.
 */
public class Merger {
    private String sourcePath;
    private String destinyPath;
    private String fileName;

    public Merger(String sourcePath, String desPath) {
        this.sourcePath = sourcePath;
        this.destinyPath = desPath;
    }

    public void StartMerge() {
        File file = new File(sourcePath);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            FileBlockInfo fileBlockInfo;
            for (int i = 0; i < files.length; i++) {
                FileInputStream fileInputStream = null;
                try {
                    fileInputStream = new FileInputStream(files[i]);
                    byte[] fileInfoData = new byte[24];
                    fileInputStream.read(fileInfoData, 0, 24);
                    fileBlockInfo = new FileBlockInfo(fileInfoData);
                    byte[] fileNameData = new byte[fileBlockInfo.getFileNameLength()];
                    fileInputStream.read(fileNameData, 0, fileNameData.length);
                    if (i == 0){
                        fileName = destinyPath + new String(fileNameData);
                        System.out.println("文件名--》"+fileName);
                        RandomAccessFile randomAccessFile=new RandomAccessFile(fileName,"rw");
                        randomAccessFile.setLength(fileBlockInfo.getAllSize());
                        randomAccessFile.close();
                    }

                    byte[] realData = new byte[fileBlockInfo.getBlockSize()];
                    fileInputStream.read(realData, 0, realData.length);
                    new MultiMergeThread(fileName, fileBlockInfo.getOffset(), realData).start();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            System.out.println("合并完成");
        }
    }

    class MultiMergeThread extends Thread {
        private long offset;
        private byte[] data;
        private String fileName;

        public MultiMergeThread(String fileName, long offset, byte[] data) {
            this.offset = offset;
            this.data = data;
            this.fileName = fileName;
        }

        @Override
        public void run() {
            RandomAccessFile randomAccessFile = null;
            try {
                randomAccessFile = new RandomAccessFile(fileName, "rw");
                randomAccessFile.seek(offset);
                randomAccessFile.write(data);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    randomAccessFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

最后就是两个测试类SpliterMain.java

package com.xiaodongliu;

import java.util.Scanner;

/**
 * Created by lxd on 16-8-20.
 */
public class SpliterMain {
    public static void main(String[] args){
        Scanner cin=new Scanner(System.in);
        String sourceFile = null;
        int blockNum = 0;
        String dstFolder = null;
        System.out.println("请输入文件全路径名...");
        while (cin.hasNext()){
            sourceFile=cin.nextLine();
            System.out.println("请输入需要分割的分数...");
            blockNum=cin.nextInt();
            cin.nextLine();
            System.out.println("请输入目标路径...");
            dstFolder=cin.nextLine();
            Splitter splitter=new Splitter(sourceFile,blockNum,dstFolder);
            splitter.StartSplit();
            System.out.println("请输入文件全路径名...");
        }
    }
}

MegerMain.java

package com.xiaodongliu;

import java.util.Scanner;

/**
 * Created by lxd on 16-8-20.
 */
public class MegerMain {
    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        String sourceFile = null;
        int blockNum = 0;
        String dstFolder = null;
        System.out.println("请输入文件全路径名...");
        while (cin.hasNext()) {
            sourceFile = cin.nextLine();

            System.out.println("请输入目标路径...");
            dstFolder = cin.nextLine();
            Merger merger = new Merger(sourceFile, dstFolder);
            merger.StartMerge();
            System.out.println("请输入文件全路径名...");
        }
    }
}

源码在 基于java的文件分割器源码

你可能感兴趣的:(java)