zoukankan      html  css  js  c++  java
  • 断点续传解决方案

    断点续传解决方案

    口水话介绍

    对于上传和下载文件,虽然http没有限制文件的大小,但是受制于环境影响因素可能造成上传或者下载中断,为了避免重新下载或者上传所带来的用户砸电脑行为的发生,一般我们会对大文件的上传和下载才去断点续传的方案

    上传流程:

    1. 上传前先将文件分成块

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

    3. 各分块全部上传完成,最后合并文件

    下载流程同理

    Java代码演示文件的分块与合并

    • 文件的分块:大致思路

      1. 获取源文件的大小

      2. 设定一个单个分块的大小

      3. 从源文件中读取数据依次向分块中写入数据

     @Test
     public void ChunkTest() throws IOException {
         File sourceFile = new File("F:\mySpace\myavi\merge.mp4");
        
         String chunkPath = "F:\mySpace\myavi\chunk\";
         File chunkFolder = new File(chunkPath);
         if(!chunkFolder.exists()){
             chunkFolder.mkdirs();
         } 
         
         //分块大小1兆
         long chunkSize = 1024*1024*1;
         //分块数量:文件大小 / 单个分块的大小,我们设置的1兆,对其向上取整
         long chunkNum = (long) Math.ceil(sourceFile.length() * 1.0 / chunkSize );
         if(chunkNum<=0){
             chunkNum = 1;
         }
         //缓冲区大小
         byte[] b = new byte[1024];
         //使用RandomAccessFile访问文件
         RandomAccessFile raf_read = new RandomAccessFile(sourceFile, "r");
         //分块
         for(int i=0;i<chunkNum;i++){
             //创建分块文件
             File file = new File(chunkPath+i);
             boolean newFile = file.createNewFile();
             if(newFile){
                 //向分块文件中写数据
                 RandomAccessFile raf_write = new RandomAccessFile(file, "rw");
                 int len = -1;
                 while((len = raf_read.read(b))!=-1){
                     raf_write.write(b,0,len);
                     if(file.length()>chunkSize){
                         break;
                     }
                 } 
                 raf_write.close();
             }
         } 
         raf_read.close();
     }
    • 文件的合并:大致思路

      1. 这里需要注意的就是:分块与合并的顺序必须一致

    @Test
     public void mergeTest() {
         try {
             //待合并的块文件目录,其下的所有文件采集
             File chunkFoler = new File("F:\mySpace\myavi\chunk\");
             File[] chunkFileArray = chunkFoler.listFiles();
             
             //合并结果文件的创建
             File mergeFile = new File("F:\mySpace\myavi\chunk\merge.mp4");
             if (mergeFile.exists()){
                 mergeFile.delete();
             }
             mergeFile.createNewFile();
                 
             //创建读写文件对象
             RandomAccessFile write = new RandomAccessFile(mergeFile, "rw");
             //指针指向文件顶端
             write.seek(0);
             //创建缓冲区
             byte[] b = new byte[1024];
             
             //因为我们分块的时候是按照1 2 3 4 这样的顺序分的,所以合并的时候也要按照这个顺序来
             //但是我们的listFiles()得到的所有的文件是没有顺序的,是乱的,所以需要排序
             List<File> fileList = Arrays.asList(chunkFileArray);
             Collections.sort(fileList, new Comparator<File>() {
                 @Override
                 public int compare(File o1, File o2) {
                     if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())){
                         return -1;
                     }
                     return 1;
                 }
             });
             for (File file : fileList) {
                 int len = -1;
                 RandomAccessFile read = new RandomAccessFile(file, "rw");
                 while ((len = read.read(b)) != -1){
                     write.write(b,0,len);
                 }
             } 
         }catch (Exception e){
             e.printStackTrace();
         }
     }

    Web Uploader + 后端思路

    这次我们使用Html5来演示分块上传,Web Uploader完成大文件上传功能演示

    官网:http://fexteam.gz01.bdysite.com/webuploader/

    • 上传流程:

    勾子方法

    webupload中提供了很多的勾子方法,比如:

    before-send-file

    • 在开始对文件分块儿之前调用,可以做一些上传文件前的准备工作,比如检查文件目录是否创建完成等。

    before-send

    • 在上传文件分块之前调用此方法,可以请求服务端检查分块是否存在,如果已存在则此分块儿不再上传。

    after-send-file

    • 在所有分块上传完成后触发,可以请求服务端合并分块文件。

    后端思路

    业务层代码我就不上了,我就写个思路:

    首先我们理一下思路,Webuploader在上传过程会调用四个API接口,他们分别是:

    • 文件上传前的注册

    • 分块文件上传前的分块检查

    • 分块文件的上传

    • 上传完成,通知合并,以及后续保存数据到数据、删除分块数据等...

    解释一下:

    • 我们的文件储存方式的路径分三级目录的,根据文件MD5值确定他的资源存放路径

    • 比如MD5值为:c5b1545123343

    • 那么该资源的第一级目录为Md5的第一个字符:c

    • 第二级目录为Md5的第二个字符:5

    • 第三级目录为MD5的值:c5b1545123343

    • 第一个模块:文件上传前的注册和相关资源文件目录的初始化

    根据MD5的值去查询该路径下是否有资源

    根据MD5去数据查询是否有资源数据

    在都没有的情况下,创建我们的资源三级目录

    • 第二个模块:分块上传前检查分块文件是否存在

    根据MD5得到相应的资源存放路径

    三级目录 + 待检查的分块id信息,得到具体的分块文件数据

    这里我们分块都是以 1、2、3这样的方式上传的的,核实待上传分块是否已经存在

    • 第三个模块:分块文件上传

    根据MD5得到三级目录,根据分块数据id得到文件名称,确定资源的绝对路径

    然后就是流的:IOUtils.copy(inputStream, fileOutputStream); 写入数据

    • 第四个模块:分块数据的合并已经后续其他操作

    根据MD5得带确定的资源存放路径,获取该路径下的所有分块数据:File[ ]

    获取到的所有分块并不是按照我们想要的顺序排列的,合成会失败

    转成集合,根据名字(1、2、3...),将文件排序

    创建合成后的文件的路径以及名称

    然后就是文件的合并,可以参考上面我写的Demo代码

    合并后我们会对这个合并文件的MD5和参数MD5再做一个校验:

    if (fileMd5.equals(DigestUtils.md5Hex(new FileInputStream(mergeFilePath))))

    然后就是将相关数据保存到数据库,以及删除分块文件、通过MQ异步对该视频进行编码,生成基于HLS协议的近实时流方式播放的视频文件(FFmpeg ?生成m3u8 / ts)...

  • 相关阅读:
    理解Linux虚拟文件系统VFS
    Linux进程间通信(IPC)
    为 区域添加 Tag
    html 模板 swig 预编译插件 grunt-swig-precompile
    如何开发 Grunt 插件
    Web开发常见的漏洞
    CSS实现不固定宽度和高度的自动居中
    Sublime Text 前端插件推荐
    JavaScript 防止事件冒泡
    HTML标签篇
  • 原文地址:https://www.cnblogs.com/msi-chen/p/11432579.html
Copyright © 2011-2022 走看看