15Java的文件分割

随机读取写入类

RandomAccessFile支持使用字节流从随机指定的位置开始读取指定长度的数据。由此可以衍生出很多功能,比如文件分块等。

看随机读取写入类RandomAccessFile的构造方法:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vI3usSvq-1630078358312)(E:(A)]PostgraduateFlies\JavaLearning\TyporaFiles\第二阶段\21Java的IO文件分割面向过程核心.assets\image-20210410223027954.png)

发现该构造方法在只能传入文件对象或者文件路径,不能传入一线流或者缓冲流,并且构造方法必须指定读取模式,由四种模式,分别是:“r”,“rw”,“rws”,rwd"。常用的是前两种,只读,同时读写。

下面是使用随机读取写入类RandomAccessFile的程序实例
package com.company;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/09 21:02
 */
public class RandomTest {
    public static void main(String[] args) throws IOException {
//        RandomRead1();
//        RandomRead2();
        RandomRead3();
    }

    //从指定位置开始,一直到文件末尾,读取文件
    public static void RandomRead1() throws IOException {
        //随机读取流的构造器只要文件路径或文件对象,不能套其他节点流或者缓冲流
        RandomAccessFile raf = new RandomAccessFile(new File("src/com/company/PrintTest.java"), "r");
        raf.seek(7);//从第七个字节开始读取,一直到文件末尾
        int len = -1;
        byte[] flush = new byte[1024];
        while ((len = raf.read(flush)) != -1) {
            System.out.println(new String(flush, 0, len));
        }
        raf.close();
    }
    
     //指定起始位置开始读取指定长度的一段内容
    public static void RandomRead2() {
        int beginPos = 10;//开始位置
        int actualSize = 1026;//这个actualSize是我想实际读取的数据大小
        try {
            RandomAccessFile raf = new RandomAccessFile(new File("src/com/company/Decorate02.java"), "r");
            raf.seek(beginPos);//定位到初始位置
            int len = -1;
            byte[] flush = new byte[1024];//每次读取的长度
            while ((len = raf.read(flush)) != -1) {
                if (actualSize > len) {//如果实际长度大于缓冲数组的长度说明一次没有读完
                    System.out.println(new String(flush, 0, len));
                    actualSize -= len;//减去此次读取的长度是为了标记最后一次读取到的有效数据
                } else {
                    System.out.println(new String(flush, 0, actualSize));
                    break;
                }
            }
            raf.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    //分块,分多少块
    public static void RandomRead3() {
        File src = new File("src/com/company/Decorate02.java");
        long len = src.length();//文件总长度
        int blockSize = 256;//每块大小
        int size = (int) Math.ceil(len * 1.0 / blockSize);//计算分多少块,ceil向上取整
        System.out.println(size);
        int beginPos = 0;//起始位置
        //这样做的目的是什么?actualSize的意思和RandomRead2()方法的意思相同,都是指实际想要读多少个字节的数据
        //做判断是因为我实际想读的数据可能比文件总字节数还要多
        //目的是找每块的起始位置和每块的实际大小
        int actualSize = (int) (blockSize > len ? len : blockSize);
        for (int i = 0; i < size; i++) {
            beginPos = i * blockSize;
            if (i == size - 1) {//最后一块
                actualSize = (int) len;
            } else {
                actualSize = blockSize;
                len -= actualSize;//剩余量
            }
            System.out.println(i + "--" + beginPos + "--" + actualSize);
            split(beginPos, actualSize);
        }
    }
	//分割方法
    public static void split(int beginPos, int actualSize) {
        try {
            RandomAccessFile raf = new RandomAccessFile(new File("src/com/company/Decorate02.java"), "r");
            raf.seek(beginPos);
            int len = -1;
            byte[] flush = new byte[256];
            while ((len = raf.read(flush)) != -1) {
                if (actualSize > len) {
                    System.out.println(new String(flush, 0, len));
                    actualSize -= len;
                } else {
                    System.out.println(new String(flush, 0, actualSize));
                    break;
                }
            }
            raf.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    
}

三个方法均能正常运行,由于运行结果是控制台输出的字符,太多,不方便截图。

使用随机读取写入类RandomAccessFile分割文本文件并写出的程序实例:
package com.company;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/09 21:02
 */
public class RandomTest02 {
    public static void main(String[] args) throws IOException {
        RandomRead3();
    }

    //分块,分多少块,每块的起始位置
    public static void RandomRead3() {
        String srcPath = "src/com/company/Decorate02.java";
        File src = new File(srcPath);
        long len = src.length();//文件总长度
        int blockSize = 256;//每块大小
        int size = (int) Math.ceil(len * 1.0 / blockSize);//计算分多少块
        System.out.println(size);
        int beginPos = 0;//起始位置
        //这样做的目的是什么?actualSize的意思和RandomRead2()方法的意思相同,都是指实际想要读多少个字节的数据
        //做判断是因为我实际想读的数据可能比文件总字节数还要多
        //目的是找每块的起始位置和每块的实际大小
        int actualSize = (int) (blockSize > len ? len : blockSize);
        for (int i = 0; i < size; i++) {
            beginPos = i * blockSize;
            if (i == size - 1) {//最后一块
                actualSize = (int) len;
            } else {
                actualSize = blockSize;
                len -= actualSize;//剩余量
            }
            System.out.println(i + "--" + beginPos + "--" + actualSize);
            split(i, beginPos, actualSize, srcPath);
        }
    }

    public static void split(int i, int beginPos, int actualSize, String srcPath) {
        try {
            RandomAccessFile raf = new RandomAccessFile(new File(srcPath), "r");
            //注意此处输出文件夹使用的是相对路径,程序识别为时候是在工程文件夹里面找,所以要先在工程文件夹里面先建立一个
            //对应的文件夹
            RandomAccessFile raf2 = new RandomAccessFile(new File("Random/"+i+"Decorate02.java"), "rw");
            raf.seek(beginPos);
            int len = -1;
            byte[] flush = new byte[256];
            while ((len = raf.read(flush)) != -1) {
                if (actualSize > len) {
                    raf2.write(flush, 0, len);
                    actualSize -= len;
                } else {
                    raf2.write(flush, 0, actualSize);
                    break;
                }
            }
            raf.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

运行结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aDCFm2eG-1630078358322)(E:(A)]PostgraduateFlies\JavaLearning\TyporaFiles\第二阶段\21Java的IO文件分割面向过程核心.assets\image-20210410224650206.png)

下面是使用面向对象的思想写分割文件(图片)的程序:
package com.company;

import java.io.IOException;

/**
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/09 21:02
 */
public class RandomTest02 {
    public static void main(String[] args) throws IOException {

        SplitFile splitFile = new SplitFile("E:/(A)PostgraduateFlies/JavaLearning/javaEE.jpg",
               "E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProject02/Random" ,4096);
        splitFile.split();
    }
}
package com.company;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 封装分割文件的程序
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/12 15:10
 */
public class SplitFile {
    private String srcPath;//源文件路径
    private String destDir;//目标文件路径
    private List<String> destPaths;//存放分割后文件的名称的List容器
    private int size;//分成多少块
    private int blockSize;//每块大小
    private long len;//源文件的总大小

    public SplitFile() {
    }

    public SplitFile(String srcPath, String destDir, int blockSize) {
        this.srcPath = srcPath;
        this.destDir = destDir;
        this.blockSize = blockSize;
        this.destPaths = new ArrayList<>();//容器实例化
        init();
    }

    private void init() {
        File src = new File(srcPath);
        this.len = src.length();//文件总长度
        this.size = (int) Math.ceil(len * 1.0 / blockSize);//计算分多少块
        System.out.println(src.getName()+"被分成了"+size+"块");

        for (int i = 0; i < size; i++) {
            this.destPaths.add(this.destDir +File.separator+ i + "-" + src.getName());//向分割后的目标文件夹添加对应的路径
        }
    }

    public void setSrcPath(String srcPath) {
        this.srcPath = srcPath;
    }

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

    public void split() {
        int actualSize = 0;
        for (int i = 0; i < size; i++) {
            int beginPos = i * blockSize;
            if (i == size - 1) {//最后一块
                actualSize = (int) len;
            } else {
                actualSize = blockSize;
                len -= actualSize;//剩余量
            }
            try {
                RandomAccessFile raf = new RandomAccessFile(this.srcPath, "r");
                raf.seek(beginPos);
                RandomAccessFile raf2 = new RandomAccessFile(this.destPaths.get(i), "rw");
                int len = -1;
                byte[] flush = new byte[256];
                while ((len = raf.read(flush)) != -1) {
                    if (actualSize > len) {
                        raf2.write(flush, 0, len);
                        actualSize -= len;
                    } else {
                        raf2.write(flush, 0, actualSize);
                        break;
                    }
                }
                raf.close();
                raf2.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:在这里插入图片描述

下面是把上面分割之后的文件再合并起来的程序:
package com.company;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 把分割之后的文件正确合并到一个文件
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/12 16:42
 */
public class combine {
    public static void main(String[] args) {
        String srcPath = "E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProject02/Random";
        String destPath="E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProject02/Random/java.jpg";
        File src = new File(srcPath);
//        List<String> srcPaths = new ArrayList<>();//源文件夹们
        File[] srcFiles = src.listFiles();
//        for (int i = 0; i < srcFiles.length; i++) {
//            srcPaths.add(srcFiles[i].getAbsolutePath());
//        }
        try {
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath,true));
            for (int i = 0; i < srcFiles.length; i++) {
//                InputStream bis = new BufferedInputStream(new FileInputStream(srcPaths.get(i)));
                InputStream bis = new BufferedInputStream(new FileInputStream(srcFiles[i]));
                int len=-1;
                byte[] flush = new byte[1024];
                while ((len = bis.read(flush)) != -1) {
                    bos.write(flush, 0, len);
                }
                bis.close();
            }
            bos.flush();
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

程序正常运行,并且合成的文件大小格式也正确,但是这个文件不能打开,暂时不清楚是什么原因。

合并流SequenceInputStream

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rlpbqWCL-1630078358328)(E:(A)]PostgraduateFlies\JavaLearning\TyporaFiles\第二阶段\21Java的IO文件分割面向过程核心.assets\image-20210412205337742.png)

把多个InputStream合并成一个序列输入流,方便操作。

下面看序列流SequenceInputStream的具体程序实例:

package com.company;

import java.io.*;
import java.util.Vector;

/**
 * 使用序列流SequenceInputStream把分割之后的文件正确合并到一个文件
 *
 * @author 发达的范
 * @version 1.0
 * @date 2021/04/12 16:42
 */
public class combine_Vector {
    public static void main(String[] args) {
        String srcPath = "E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProject02/Random";
        String destPath = "E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProject02/Random/java.jpg";
        File src = new File(srcPath);
//        List<String> srcPaths = new ArrayList<>();//源文件夹们
        File[] srcFiles = src.listFiles();
//        for (int i = 0; i < srcFiles.length; i++) {
//            srcPaths.add(srcFiles[i].getAbsolutePath());
//        }
        try {
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath, true));
            Vector<InputStream> vi = new Vector<>();//Vector是一个容器,泛型为输入流
            SequenceInputStream sis = null;//序列流,把所有的流集中处理
            for (int i = 0; i < srcFiles.length; i++) {
                vi.add(new BufferedInputStream(new FileInputStream(srcFiles[i])));
            }
            sis = new SequenceInputStream(vi.elements());//把Vector容器中的所有输入流实例化成
            												//一个SequenceInputStream的对象
            int len = -1;
            byte[] flush = new byte[1024];
            while ((len = sis.read(flush)) != -1) {
                bos.write(flush, 0, len);
            }
            bos.flush();//关闭流之前先flush一下,放防止内存中滞留数据
            sis.close();
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这个程序也能正常运行,并且合成的文件大小格式也正确,但是这个文件也不能打开。

具体分析序列流的用法就是:

  1. 使用一个Vector容器添加泛型为,专门存放InputStream流;
  2. 实例化一个序列流SequenceInputStream对象,然后把Vector容器中所有的InputStream添加(add)到序列流对象中;
  3. 直接使用过序列流对象读取所有的输入流的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>