大文件RandomAccessFile类来分片传输

23 篇文章 1 订阅

RandomAccessFile简单使用

    /**
     * @param :
     * @return void
     * @author Ladidol
     * @description 文件的续写.
     * @date 2022/6/30 14:35
     */
    @Test
    public void fileContinue() {

        File preFile = new File("D:\\randomaccessfiletest\\pre.txt");
        File endFile = new File("D:\\randomaccessfiletest\\end.txt");


        try (
                RandomAccessFile r = new RandomAccessFile(endFile, "r");
                RandomAccessFile w = new RandomAccessFile(preFile, "rw");
        ) {

            w.seek(w.length()); // 将指针指向文件最后,进行追加
            byte[] bytes = new byte[1024];
            int len = -1;
            while ((len = r.read(bytes)) != -1) {
                w.write(bytes);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

大文件单线程直接传输

    /**
     * @param :
     * @return void
     * @author Ladidol
     * @description 单线程文件读写. 耗时长, 且主线程堵塞.
     * @date 2022/6/29 21:03
     */
    @Test
    public void fileReadAndWrite() throws IOException {
        //开始传输时间
        long start = System.currentTimeMillis();
        File preFile = new File("D:\\迅雷云盘\\[公众号:分派电影]V字仇杀队.V.for.Vendetta.2005.BD1080P.中英双字.mp4");//用一个很大的文件, 比如电影来传.
        String readFile = preFile.getAbsolutePath();
        String writeFile = preFile.getParent() + File.separator + "单线程传输-" + start + ".mp4";//File.separator相当于路径分隔符
        byte[] bytes = new byte[2048];
        try (RandomAccessFile r = new RandomAccessFile(new File(readFile), "r");
             RandomAccessFile w = new RandomAccessFile(new File(writeFile), "rw");) {
            int len = -1;
            while ((len = r.read(bytes)) != -1) {
                w.write(bytes);
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("(end - start) = " + (end - start));

        /*传输测试时间结果(end - start) = 9520*/

    }

RandomAccessFile实现大文件分片传输

    /**
     * @param :
     * @return void
     * @author Ladidol
     * @description 多线程分片对同一个文件进行读和写
     * @date 2022/6/29 21:14
     */

    @Test
    public void multiThreadReadAndWrite() throws ExecutionException, InterruptedException {

        //开始传输时间
        long start = System.currentTimeMillis();

        File preFile = new File("D:\\迅雷云盘\\[公众号:分派电影]V字仇杀队.V.for.Vendetta.2005.BD1080P.中英双字.mp4");
        String readFile = preFile.getAbsolutePath();//文件路径
        String writeFile = preFile.getParent() + File.separator + "多线程传输-" + start + ".mp4";//新文件名字
        long length = new File(readFile).length(); // 文件一共大小
        int slice = (int) length / 8; // 将文件分成8各部分来传.
        System.out.println("sliceNum = " + slice);
        List<long[]> longs = new ArrayList<>(8);
        for (int i = 0; i < 8; i++) {
            longs.add(new long[]{i * slice});
        }

        //开启线程.
        ExecutorService executorService = Executors.newCachedThreadPool();
        List<Future<?>> futures = new ArrayList<>();
        for (long[] aLong : longs) {
            long l = aLong[0]; // 起始位置
            // 创建线程并运行
            Future<?> randomAccessFile =
//                    ExecutorUtils.createFuture(
                    executorService.submit(
                            () -> {
                                byte[] bytes = new byte[slice];
                                try (
                                        // 在线程内部创RandomAccessFile对象
                                        RandomAccessFile r = new RandomAccessFile(new File(readFile), "r");
                                        RandomAccessFile w = new RandomAccessFile(new File(writeFile), "rw");) {

                                    r.seek(l);
                                    int len = r.read(bytes);
                                    if (len < slice) {
                                        // 调整数组, 避免出现问题.
                                        bytes = getActualBytes(bytes,len);
                                    }

                                    // 写入文件
                                    w.seek(l);
                                    w.write(bytes);
                                    System.out.println("当前线程读写起点: " + l);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                                //callable的返回值
                                return "call return " + Thread.currentThread().getName();
                            });
            futures.add(randomAccessFile);
        }

        int count = 0;
        for (Future<?> future : futures) {
            count++;
            if (!future.isDone()) {
                System.out.println("资源还没有准备好" + count);
            }
            if (future.isDone()) {
                System.out.println("资源准备好了捏!" + count);
            }
            System.out.println(future.get());
        }

        // 阻塞全部线程执行完毕
        executorService.shutdown();
        long end = System.currentTimeMillis();
        System.out.println("(end - start) = " + (end - start));

        /*传输测试时间结果(end - start) = 2377*/

    }


    /**
     * @param bytes:
     * @param len:
     * @return byte
     * @author Ladidol
     * @description 截取真实长度的数组.
     * @date 2022/6/30 14:07
     */
    byte[] getActualBytes(byte[] bytes, int len) {
        byte[] b1 = new byte[len];
        System.arraycopy(bytes, 0, b1, 0, len);
        return b1;
    }
文件夹情况:

image-20220630145545196

多线程使用RandomAccessFile

常见的使用多线程的场景: 断点续传和断点下载,或者文件加密解密等

断点续传原理就是:

  1. 前端将文件安装百分比进行计算,每次上传文件的百分之一(文件分片),给文件分片做上序号
  2. 后端将前端每次上传的文件,放入到缓存目录
  3. 等待前端将全部的文件内容都上传完毕后,发送一个合并请求
  4. 后端使用RandomAccessFile进多线程读取所有的分片文件,一个线程一个分片
  5. 后端每个线程按照序号将分片的文件写入到目标文件中,
  6. 在上传文件的过程中发生断网了或者手动暂停了,下次上传的时候发送续传请求,让后端删除最后一个分片
  7. 前端重新发送上次的文件分片

END

参考链接:

Java-Io-RandomAccessFile(任意位置读写数据)_胡安民的博客-CSDN博客

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兴趣使然的小小

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值