文件的分割与合并

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

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

  • RandomAccessFile(File file,String mode);
  • RandomAccessFile(String filePath,String mode);

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

  • "r":以只读方式打开。调用结果对象的任何write方法都将导致抛出 IOException。

  • "rw":打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。

  • "rws":打开以便读取和写入,对于"rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。

  • "rwd" :打开以便读取和写入,对于"rw",还要求对文件内容的每个更新都同步写入到底层存储设备。

方法:就用了一个,其他的自己看,复习的时候记得看看
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);
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,605评论 18 399
  • permissionn.允许;批准,正式认可,认可user permission 使用权限permission D...
    lengol阅读 1,040评论 0 51
  • 本篇文章主要介绍RandomAccessFile,该类是IO流体系中功能最丰富的文件内容访问类,既可以读取文件内容...
    Ruheng阅读 52,759评论 3 65
  • maxwell2ic阅读 850评论 0 0