文件的分割与合并

2016-11-12  本文已影响0人  VikingOldYoung

我极限了,这个程序讲不清。我每一步都有分析,仔细看程序
设计这样的程序,都是一步一步的走,感觉少什么再完善修改前面的。但是主体的步骤要定好。

这里用到了RandomAccessFile这个类。这也是一个流。此类的实例支持对随机访问文件的读取和写入。他直接继承于Object
构造器

这里mode,下面两个具体看API,我们这里只用了r

方法:就用了一个,其他的自己看,复习的时候记得看看
seek();设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
<br />
程序:这玩意啥都能分,分的时候注意文件的大小,给定的分割块的大小太小的话,那就很多文件了。看着贼麻烦。

public class FileSplit {
    // 文件路径
    private String filePath;
    // 文件名
    private String fileName;
    // 文件总长度
    private long length;
    // 保存的路径
    private String destPath;
    // 快数
    private int size;
    // 每块的大小
    private long blockSize;
    // 每块的路径,存到一个数组里
    private List<String> blockPath;

    public FileSplit() {
        this.blockPath = new ArrayList<>();
    }

    public FileSplit(String filePath) {
        this(filePath, 1024, new File(filePath).getParent());
    }
    //这里搞个默认的储存点,就是原文件那个目录下
    public FileSplit(String filePath, long blockSize) {
        this(filePath, blockSize, new File(filePath).getParent());
    }

    public FileSplit(String filePath, long blockSize, String destPath) {
        this();
        this.filePath = filePath;
        this.blockSize = blockSize;
        if(!new File(destPath).isDirectory())
            try {
                throw new Exception();
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("保存的路径必须为目录");
            }
        this.destPath = destPath;
        //这里初始化直接在构造时候就完成了,因为这就俩方法,肯定构造的目的就是要分割。所以也没啥事
        init();
    }

    /**
     * 初始化操作,计算块数,确定文件名
     */
    private void init() {
        //先把那个文件搞出来,方便判断,是个局部变量,这个方法结束也就没有了。
        File src = new File(filePath);

        if (null == filePath || !src.exists()) {
            return;
        }
        if (src.isDirectory()) {
            return;
        }
        // 获取文件名
        this.fileName = src.getName();
        // 获取文件的大小
        this.length = src.length();

        // 修正每块的大小
        if (blockSize > length) {
            //这里如果大小大于文件的长度的话,就得修正他了
            blockSize = length;
        }
        //利用了Math.ceil得到整数的块数
        size = (int) Math.ceil(length * 1.0 / blockSize);
        //调用私有方法,初始化每个文件的名字
        initBlockPathName();
    }

    private void initBlockPathName() {
        for (int i = 0; i < size ; i++)
            //这里每个文件的地址,就是目的地址加上文件名啦
            blockPath.add(destPath + "/" + fileName + ".part" + i);
    }

    /**
     * 分割文件,起始点,每块实际大小,块数
     */
    public void split() {
        //起始点一开始是0
        long beginPos = 0;
        //每块实际大小一开始就等于给的大小
        long actualBlockSize = blockSize;
        
        //挨个来分割啦
        for (int i = 0; i < size ; i++) {
            //如果是最后一块,那么就存在剩的不够的情况,改变实际大小为剩的大小
            if (i == size - 1) {
                //总大小减去该块的起始点
                actualBlockSize = this.length - beginPos;
            }
            splitDetil(i, beginPos, actualBlockSize);
            //每次分割的起始点,都是前一块的起始点,加上实际分割的块的大小
            beginPos += actualBlockSize;
        }

    }
    
    /**
     * 分割的细节
     * @param index 索引,第几块
     * @param beginPos 相对于源文件开始的位置
     * @param actualBlockSize 分割的实际块大小
     */
    public void splitDetil(int index, long beginPos, long actualBlockSize) {
        //建立文件联系
        File src = new File(this.filePath);
        //获得初始化的那块的文件名
        File dest = new File(this.blockPath.get(index));
        //选择流
        RandomAccessFile raf = null;
        BufferedOutputStream bos = null;
        try {
            raf = new RandomAccessFile(src, "r");
            bos = new BufferedOutputStream(new FileOutputStream(dest));
            //跳到起始点
            raf.seek(beginPos);
            //读取部分源文件,保存到目标文件
            byte[] flush = new byte[1024];
            int len = 0;
            // 如果定义的blockSize大于flush的存储量,那么下面的判断就有必要了
            while (-1 != (len = raf.read(flush))) {
                // 如果大于的话,把len长度的写进去,再接着读,但是blockSize就得减少了
                if (actualBlockSize - len > 0) {
                    bos.write(flush, 0, len);
                    actualBlockSize -= len;
                } else { 
                    // 如果到最后了,不足len,那么就写actualBlockSize,
                    //然后直接跳出读取的循环,因为后面文件还是有内容的,但是这一块已经够了,所以要跳出去
                    bos.write(flush, 0, (int) actualBlockSize);
                    break;
                }
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            CloseUtil.closeIO(bos, raf);
        }

    }
    /**
     * 合并文件
     */
    //如果未指定路径就保存在原文件的路径下
    public void mergeFile(){
        mergeFile(this.destPath);
    }
    //给顶指定的路径
    public void mergeFile(String destPath) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos=null;
        
        File dest=new File(destPath);
        //如果给的是一个路径的话,就以原来文件的名字加个new-来新建文件
        if(!dest.isFile()){
            dest=new File(destPath,"merge-"+fileName);
        }
        
        try {
            for (int i = 0; i < size ; i++) {
                //这里读取保存的每个文件的名字
                File src = new File(this.blockPath.get(i));
                //输入源是那些文件
                bis = new BufferedInputStream(new FileInputStream(src));
                bos=new BufferedOutputStream(new FileOutputStream(dest,true));
                //正常输出到目标文件,这里需要追加,在上面的FileOutputStream里面标识了
                byte[] flush=new byte[1024];
                int len=0;
                while(-1!=(len=bis.read(flush))){
                    bos.write(flush,0,len);
                }
                //刷新是个好习惯
                bos.flush();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //关闭资源
            CloseUtil.closeIO(bos,bis);
        }
    }

    public static void main(String[] args) {
        FileSplit fs = new FileSplit("f:/javaIotest/poetry.txt", 30);
        fs.split();
        fs.mergeFile();
    }
}

<br />
关于文件的合并,里面用到了很多的输入流,每个分开操作。可以使用一个SequenceInputStream来把那些输入流合并到一起。然后当作一个流来输入。方便。以下是用法。

public void mergeFile2(String destPath) {

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        // SequenceInputStream的构造起要求是个枚举
        SequenceInputStream sis = null;
        // 这里选择继承了枚举接口的容器,Vector。把InputStream放进去
        Vector<InputStream> v = new Vector<>();

        File dest = new File(destPath);
        // 如果给的是一个路径的话,就以原来文件的名字加个new-来新建文件
        if (!dest.isFile()) {
            dest = new File(destPath, "merge-" + fileName);
        }

        try {
            for (int i = 0; i < size; i++) {
                // 这里读取保存的每个文件的名字
                File src = new File(this.blockPath.get(i));
                // 输入源是那些文件
                bis = new BufferedInputStream(new FileInputStream(src));
                v.add(bis);
            }
            sis = new SequenceInputStream(v.elements());
            bos = new BufferedOutputStream(new FileOutputStream(dest, true));

            byte[] flush = new byte[1024];
            int len = 0;
            while (-1 != (len = sis.read(flush))) {
                bos.write(flush, 0, len);

                bos.flush();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            CloseUtil.closeIO(sis, bos, bis);
        }
    }
上一篇下一篇

猜你喜欢

热点阅读