视频断点上传

news2024/11/17 10:45:10

什么是断点续传

通常视频文件都比较大,所以对于媒资系统上传文件的需求要满足大文件的上传要求。http协议本身对上传文件大小没有限制,但是客户的网络环境质量、电脑硬件环境等参差不齐,如果一个大文件快上传完了网断了没有上传完成,需要客户重新上传,用户体验非常差,所以对于大文件上传的要求最基本的是断点续传。

什么是断点续传:

        引用百度百科:断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载,断点续传可以提高节省操作时间,提高用户体验性。

断点续传流程如下图:

流程如下:

1、前端上传前先把文件分成块

2、一块一块的上传,上传中断后重新上传,已上传的分块则不用再上传

3、各分块上传完成最后在服务端合并文件

 文件分块

文件分块的流程如下:

1、获取源文件长度

2、根据设定的分块文件的大小计算出块数

3、从源文件读数据依次向每一个块文件写数据。

/**
     * 文件分块上传测试
     */
    @Test
    public  void  testChunk(){
        //获取源文件
        File sourceFile = new File("B:\\workspace\\test\\you.ncm");
        //源文件字节大小
        long length = sourceFile.length();
        //分块文件目录
        String chunkPath="B:\\workspace\\test\\chunk\\";
        File chunkFolder = new File(chunkPath);
        //检查目录是否存在
        if (!chunkFolder.exists()) {
            //不存在就创建
            chunkFolder.mkdirs();
        }
        //分块大小
        long chunkSize = 1024*1024*1;
        //分块数量
        long chunkNum = (long) Math.ceil(length * 1.0 / chunkSize);
        //缓冲区大小
        byte[] b = new byte[1024];
        //使用RandomAccessFile访问文件
        try {
            RandomAccessFile read = new RandomAccessFile(sourceFile, "r");
            for (int i = 0; i < chunkNum; i++) {
                //创建分块文件
                File file = new File(chunkPath + i);
                //检查文件是否存在,如果存在就删除文件
                if(file.exists()){
                    file.delete();
                }
                //创建一个新文件
                boolean newFile = file.createNewFile();
                if (newFile){
                    //向分块文件中写数据
                    RandomAccessFile write = new RandomAccessFile(file,"rw");
                    int len = -1;
                    while ((len = read.read(b)) != -1) {
                        write.write(b, 0, len);
                        //如果分块文件的大小大于等于分块大小就跳过本次循环
                        if (file.length() >= chunkSize) {
                            break;
                        }
                    }
                    write.close();
                    System.out.println("完成分块"+i);
                }

            }
            read.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

RandomAccessFile 是 Java 中的一个类,它允许对文件的任意位置进行读写操作。与其他的输入/输出流(如 InputStream 和 OutputStream)不同,RandomAccessFile 并不属于它们的类系,而是直接继承自 Object 类。它提供了类似于文件系统中的随机访问功能,因此得名“随机访问文件”。

以下是 RandomAccessFile 的一些主要特点和功能:

  1. 随机访问RandomAccessFile 允许你直接跳到文件的任意位置来读写数据。这是通过使用 seek(long pos) 方法实现的,它可以将文件的指针移动到指定的位置。
  2. 读写功能RandomAccessFile 既可以从文件中读取数据,也可以向文件中写入数据。它提供了类似于 InputStream 的 read() 方法和类似于 OutputStream 的 write() 方法来执行这些操作。
  3. 文件指针操作:除了 seek(long pos) 方法外,RandomAccessFile 还提供了 getFilePointer() 方法来返回文件记录指针的当前位置。
  4. 访问模式:在创建 RandomAccessFile 对象时,你需要指定一个访问模式,它决定了文件是以只读方式打开还是以读写方式打开。常见的访问模式有 "r"(只读)和 "rw"(读写)。
  5. 文件操作模式:在 JDK 1.6 及更高版本中,RandomAccessFile 还支持 "rws" 和 "rwd" 模式。在 "rws" 模式下,每次写入操作都会确保数据被写入到磁盘中;而在 "rwd" 模式下,只有在对文件执行了某些特定的更新操作(如关闭文件或调用 flush() 方法)后,数据才会被写入到磁盘中。
  6. 内存映射文件:虽然 RandomAccessFile 提供了强大的文件访问功能,但在某些情况下,使用 JDK 1.4 引入的“内存映射文件”可能会更高效。内存映射文件允许你将文件的一部分或全部映射到内存中,从而可以像访问内存一样快速地访问文件。

文件合并 

文件合并流程:

1、找到要合并的文件并按文件合并的先后进行排序。

2、创建合并文件

3、依次从合并的文件中读取数据向合并文件写入数

文件合并的测试代码 :

//测试文件合并方法
    @Test
    public void testMerge(){
        try {
            //获取源文件
            File sourceFile = new File("B:\\workspace\\test\\you.ncm");
            //分块文件目录
            String chunkPath="B:\\workspace\\test\\chunk\\";
            //合并后的文件
            File mergeFile = new File("B:\\workspace\\test\\you1.ncm");
            if (mergeFile.exists()) {
                mergeFile.delete();
            }
            //创建新的合并文件
            mergeFile.createNewFile();
            RandomAccessFile write = new RandomAccessFile(mergeFile,"rw");
            //指针指向文件顶端
            write.seek(0);
            //缓冲区
            byte[] b = new byte[1024];
            //获取分块文件数组
            File file = new File(chunkPath);
            File[] files = file.listFiles();
            // 转成集合,便于排序
            List<File> fileList = Arrays.asList(files);
            //使用工具类和自定义比较类进行排序

            Collections.sort(fileList, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    Integer o1Name = Integer.parseInt(o1.getName());
                    Integer o2Name=Integer.parseInt(o2.getName());
                    return o1Name-o2Name;
                }
            });
            //合并文件
            for (File file1 : fileList) {
                RandomAccessFile read = new RandomAccessFile(file1,"r");
                int len = -1;
                while ((len = read.read(b)) != -1) {
                    write.write(b, 0, len);

                }
                read.close();
            }
            write.close();
            //校验文件
            FileInputStream fileInputStream = new FileInputStream(sourceFile);
            FileInputStream mergeFileStream = new FileInputStream(mergeFile);
            //取出原始文件的md5
            String originalMd5 = DigestUtils.md5Hex(fileInputStream);
            //取出合并文件的md5进行比较
            String mergeFileMd5 = DigestUtils.md5Hex(mergeFileStream);
            if (originalMd5.equals(mergeFileMd5)) {
                System.out.println("合并文件成功");
            } else {
                System.out.println("合并文件失败");
            }





        } catch (Exception e) {
            e.printStackTrace();
        }


    }

视频上传流程 

 

1、前端对文件进行分块。

2、前端上传分块文件前请求媒资服务检查文件是否存在,如果已经存在则不再上传。

3、如果分块文件不存在则前端开始上传

4、前端请求媒资服务上传分块。

5、媒资服务将分块上传至MinIO。

6、前端将分块上传完毕请求媒资服务合并分块。

7、媒资服务判断分块上传完成则请求MinIO合并文件。

8、合并完成校验合并后的文件是否完整,如果不完整则删除文件。

 

测试将分块文件上传至minio

 //将分块文件上传至minio
    @Test
    public void uploadChunk(){
        String chunkFolderPath = "B:\\workspace\\test\\chunk\\";
        File chunkFolder = new File(chunkFolderPath);
        //分块文件
        File[] files = chunkFolder.listFiles();
        //将分块文件上传至minio
        for (int i = 0; i < files.length; i++) {
            try {
                UploadObjectArgs uploadObjectArgs = UploadObjectArgs
                                                      .builder()
                                                      .bucket("testbucket")//桶名
                                                      .object("chunk/" + i)//存储路径+文件名
                                                      .filename(files[i].getAbsolutePath())
                                                      .build();
                minioClient.uploadObject(uploadObjectArgs);
                System.out.println("上传分块成功"+i);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

测试通过minio的合并文件

 

//合并文件,要求分块文件最小5M
    @Test
    public void test_merge() throws Exception {
        List<ComposeSource> sources = new ArrayList<>();
        for (int i = 0; i <=7; i++) {
            ComposeSource composeSource = ComposeSource
                    .builder()//指定分块文件信息
                    .bucket("testbucket")
                    .object("chunk/" + (Integer.toString(i)))//目标文件信息
                    .build();
            sources.add(composeSource);
        }
        ComposeObjectArgs composeObjectArgs = ComposeObjectArgs
                                                        .builder()
                                                        .bucket("testbucket")
                                                        .object("merge01.npm")//目标文件
                                                        .sources(sources)//源文件
                                                        .build();
        //合并文件
        minioClient.composeObject(composeObjectArgs);

    }

测试minio清除分块文件 

//清除分块文件
    @Test
    public void test_removeObjects(){
        //合并分块完成将分块文件清除
        List<DeleteObject> deleteObjects = Stream.iterate(0, i -> ++i)
                .limit(2)//循环几次
                .map(i -> new DeleteObject("chunk/".concat(Integer.toString(i))))
                .collect(Collectors.toList());

        RemoveObjectsArgs removeObjectsArgs = RemoveObjectsArgs.builder().bucket("testbucket").objects(deleteObjects).build();
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(removeObjectsArgs);
        results.forEach(r->{
            DeleteError deleteError = null;
            try {
                deleteError = r.get();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1665288.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式事务?哪几种方式实现?一文看懂!

什么是分布式事务 分布式事务是指在分布式系统中涉及到多个数据库或多个应用程序之间的事务处理&#xff0c;这些数据库或应用程序可能分布在不同的物理节点上&#xff0c;甚至可能位于不同的地理位置。在分布式事务中&#xff0c;需要确保所有参与者的事务操作都能够保持一致性…

SNMPv3-原理浅谈+报文示例+简易配置

个人认为&#xff0c;理解报文就理解了协议。通过报文中的字段可以理解协议在交互过程中相关传递的信息&#xff0c;更加便于理解协议。 因此本文将在 SNMPv3 协议报文的基础上进行介绍。 SNMPv3 相关 RFC 文档。 关于 SNMPv3 的基本内容介绍&#xff0c;可参考RFC3410-Intro…

vue3中如何更优雅的使用echarts?

echarts在vue或者react中使用存在的问题 每个图表需要从头到尾写地一遍完整的option配置&#xff0c;这样一来的话就会显得十分的冗余在同一个项目中&#xff0c;其实不难发现各类图表设计十分相似&#xff0c;甚至是相同&#xff0c;因此我们没必要一直做重复的工作&#xff…

2.2、Gitea忘记密码重置密码

忘记密码后&#xff0c;管理员可以使用gitea的主程序输入命令重置密码。 gitea admin user change-password --username myname --password asecurepassword

Python多线程与互斥锁模拟抢购余票的示例

一、示例代码&#xff1a; from threading import Thread from threading import Lock import timen 100 # 共100张票def task():global nmutex.acquire() # 上锁temp ntime.sleep(0.1)n temp - 1print(购票成…

树的基本介绍

引入 定义 表示 相关概念 结点&#xff1a;数据元素与指向分支的指针两部分组成 树的深度&#xff1a;树中结点的最大层次 将树A结点(根结点)去掉&#xff0c;树A就变成了森林 区别 实现

vuex核心概念-actions

目录 一、概述 二、应用场景 三、使用步骤 三、注意 四、辅助函数-mapActions 一、概述 目标&#xff1a;明确actions的基本语法&#xff0c;处理异步操作。 需求&#xff1a;一秒钟之后&#xff0c;修改state的count成666。 说明&#xff1a;mutations必须是同步的(便于…

Softing工业推出的edgeConnector将Allen-Bradley控制器集成到工业边缘应用中

2024年4月17日&#xff08;哈尔&#xff09;&#xff0c;Softing宣布扩展其基于Docker的edgeConnector产品系列&#xff0c;推出了新软件模块edgeConnector Allen Bradley PLC&#xff0c;可方便用户访问来自ControlLogix和CompactLogix控制器数据。 &#xff08;edgeConnector…

【小红书采集工具】根据搜索关键词批量采集小红书笔记,含笔记正文、笔记链接、发布时间、转评赞藏等

一、背景介绍 1.1 爬取目标 熟悉我的小伙伴都了解&#xff0c;我之前开发过2款软件&#xff1a; 【GUI软件】小红书搜索结果批量采集&#xff0c;支持多个关键词同时抓取&#xff01; 【GUI软件】小红书详情数据批量采集&#xff0c;含笔记内容、转评赞藏等&#xff0c;支持…

从诊室到云端:医疗大模型的应用挑战与未来探索

从诊室到云端&#xff1a;医疗大模型的应用挑战与未来探索 2023年是中国医疗大模型发展的元年&#xff0c;各种医疗大模型已广泛应用于临床辅助决策、医学研究、健康管理等多个场景。未来&#xff0c;医疗大模型有望实现多模态AI与医疗实践全流程的深入链接&#xff0c;应用于医…

LibreNMS简介

目录 1 LibreNMS简单介绍1.1 LibreNMS介绍 2 安装2.1 Ubuntu安装1、安装依赖2、添加 librenms 用户3、下载 LibreNMS4、设置权限5、安装 PHP 依赖项6、设置时区7、配置 MariaDB8、配置 PHP-FPM9、配置 Web 服务器10、启用 lnms 命令11、配置 snmpd12、cron13、启用调度程序14、…

OS复习笔记ch5-2

引言 在上一篇笔记中&#xff0c;我们介绍到了进程同步和进程互斥&#xff0c;以及用硬件层面上的三种方法分别实现进程互斥。其实&#xff0c;软件层面上也有四种方法&#xff0c;但是这些方法大部分都存在着一些问题&#xff1a; “上锁”与“检查”是非原子操作&#xff0…

Kafka学习-Java使用Kafka

文章目录 前言一、Kafka1、什么是消息队列offset 2、高性能topicpartition 3、高扩展broker 4、高可用replicas、leader、follower 5、持久化和过期策略6、消费者组7、Zookeeper8、架构图 二、安装Zookeeper三、安装Kafka四、Java中使用Kafka1、引入依赖2、生产者3、消费者4、运…

盛邦安全荣获北京市海淀区上地街道财源建设工作表彰

近日&#xff0c;盛邦安全受邀出席上地街道2024年第一季度财源建设工作联席会暨上地人工智能产业报告发布大会并收到上地街道颁发的感谢信&#xff0c;这是对公司技术创新、管理提升、营收增长&#xff0c;持续为上地地区财源建设做出突出贡献的鼓励。 盛邦安全副总裁、董事会秘…

第四百九十九回

文章目录 1. 概念介绍2. 使用方法2.1 固定样式2.2 自定义样式 3. 示例代码4. 内容总结 我们在上一章回中介绍了"GetMaterialApp组件"相关的内容&#xff0c;本章回中将介绍使用get显示SnackBar.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在介…

pyqt颜色变换动画效果

pyqt颜色变换动画效果 QPropertyAnimation介绍颜色变换效果代码 QPropertyAnimation介绍 QPropertyAnimation 是 PyQt中的一个类&#xff0c;它用于对 Qt 对象的属性进行动画处理。通过使用 QPropertyAnimation&#xff0c;你可以平滑地改变一个对象的属性值&#xff0c;例如窗…

【企业必备】提升企业新质生产力,英智推出私有化大模型定制方案

市面上有很多性能不错的大模型&#xff0c;但大部分企业都不敢使用&#xff0c;担心模型训练和使用过程中出现数据泄露的风险。智能化升级是当今时代企业发展的必然趋势&#xff0c;在大模型发展迅猛的时代&#xff0c;借助AI能力的公司能够更快速提升自身的核心竞争力&#xf…

使用xtuner微调InternLM-Chat-7B

1. 安装xtuner #激活环境 source activate test_llm # 安装xtuner pip install xtuner#还有一些依赖项需要安装 future>0.6.0 cython lxml>3.1.0 cssselect mmengine 2. 创建一个ft-oasst1 数据集的工作路径&#xff0c;进入 mkdir ft-oasst1 cd ft-oasst1 3.XTune…

以Azure为例的SSO

由于文章的篇幅有限&#xff0c;无法将全部的代码贴上来&#xff0c;如想要看完整案例&#xff0c;请在公众号文章中留言(其他平台很少看…毕竟最近印度同事的UI组件库搞得我好烦) 1.关于SSO 单点登录又称之为SSO,全称为 Single Sign On &#xff0c;一般在多个应用系统中&…

LangChain连接国内大模型测试|智谱ai、讯飞星火、通义千问

智谱AI 配置参考 https://python.langchain.com/v0.1/docs/integrations/chat/zhipuai/ZHIPUAI_API_KEY从https://open.bigmodel.cn/获取 from langchain_community.chat_models import ChatZhipuAI from langchain_core.messages import AIMessage, HumanMessage, SystemMes…