大文件上传做断点续传(有详细的代码内容)

news2024/11/28 23:44:30

文章目录

    • 一、是什么
        • 分片上传
        • 断点续传
    • 二、实现思路
    • 三、使用场景
    • 小结
    • 参考文献

在这里插入图片描述

一、是什么

不管怎样简单的需求,在量级达到一定层次时,都会变得异常复杂

文件上传简单,文件变大就复杂

上传大文件时,以下几个变量会影响我们的用户体验

  • 服务器处理数据的能力
  • 请求超时
  • 网络波动

上传时间会变长,高频次文件上传失败,失败后又需要重新上传等等

为了解决上述问题,我们需要对大文件上传单独处理

这里涉及到分片上传及断点续传两个概念

分片上传

分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(Part)来进行分片上传

如下图

上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件

大致流程如下:

  1. 将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
  2. 初始化一个分片上传任务,返回本次分片上传唯一标识;
  3. 按照一定的策略(串行或并行)发送各个分片数据块;
  4. 发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件
断点续传

断点续传指的是在下载或上传时,将下载或上传任务人为的划分为几个部分

每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。用户可以节省时间,提高速度

一般实现方式有两种:

  • 服务器端返回,告知从哪开始
  • 浏览器端自行处理

上传过程中将文件在服务器写为临时文件,等全部写完了(文件上传完),将此临时文件重命名为正式文件即可

如果中途上传中断过,下次上传的时候根据当前临时文件大小,作为在客户端读取文件的偏移量,从此位置继续读取文件数据块,上传到服务器从此偏移量继续写入文件即可

二、实现思路

整体思路比较简单,拿到文件,保存文件唯一性标识,切割文件,分段上传,每次上传一段,根据唯一性标识判断文件上传进度,直到文件的全部片段上传完毕

下面的内容都是伪代码

读取文件内容:

const input = document.querySelector('input');
input.addEventListener('change', function() {
    var file = this.files[0];
});

可以使用md5实现文件的唯一性

const md5code = md5(file);

然后开始对文件进行分割

var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.addEventListener("load", function(e) {
    //每10M切割一段,这里只做一个切割演示,实际切割需要循环切割,
    var slice = e.target.result.slice(0, 10*1024*1024);
});

h5上传一个(一片)

const formdata = new FormData();
formdata.append('0', slice);
//这里是有一个坑的,部分设备无法获取文件名称,和文件类型,这个在最后给出解决方案
formdata.append('filename', file.filename);
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function() {
    //xhr.responseText
});
xhr.open('POST', '');
xhr.send(formdata);
xhr.addEventListener('progress', updateProgress);
xhr.upload.addEventListener('progress', updateProgress);

function updateProgress(event) {
    if (event.lengthComputable) {
        //进度条
    }
}

这里给出常见的图片和视频的文件类型判断

function checkFileType(type, file, back) {
/**
* type png jpg mp4 ...
* file input.change=> this.files[0]
* back callback(boolean)
*/
    var args = arguments;
    if (args.length != 3) {
        back(0);
    }
    var type = args[0]; // type = '(png|jpg)' , 'png'
    var file = args[1];
    var back = typeof args[2] == 'function' ? args[2] : function() {};
    if (file.type == '') {
        // 如果系统无法获取文件类型,则读取二进制流,对二进制进行解析文件类型
        var imgType = [
            'ff d8 ff', //jpg
            '89 50 4e', //png

            '0 0 0 14 66 74 79 70 69 73 6F 6D', //mp4
            '0 0 0 18 66 74 79 70 33 67 70 35', //mp4
            '0 0 0 0 66 74 79 70 33 67 70 35', //mp4
            '0 0 0 0 66 74 79 70 4D 53 4E 56', //mp4
            '0 0 0 0 66 74 79 70 69 73 6F 6D', //mp4

            '0 0 0 18 66 74 79 70 6D 70 34 32', //m4v
            '0 0 0 0 66 74 79 70 6D 70 34 32', //m4v

            '0 0 0 14 66 74 79 70 71 74 20 20', //mov
            '0 0 0 0 66 74 79 70 71 74 20 20', //mov
            '0 0 0 0 6D 6F 6F 76', //mov

            '4F 67 67 53 0 02', //ogg
            '1A 45 DF A3', //ogg

            '52 49 46 46 x x x x 41 56 49 20', //avi (RIFF fileSize fileType LIST)(52 49 46 46,DC 6C 57 09,41 56 49 20,4C 49 53 54)
        ];
        var typeName = [
            'jpg',
            'png',
            'mp4',
            'mp4',
            'mp4',
            'mp4',
            'mp4',
            'm4v',
            'm4v',
            'mov',
            'mov',
            'mov',
            'ogg',
            'ogg',
            'avi',
        ];
        var sliceSize = /png|jpg|jpeg/.test(type) ? 3 : 12;
        var reader = new FileReader();
        reader.readAsArrayBuffer(file);
        reader.addEventListener("load", function(e) {
            var slice = e.target.result.slice(0, sliceSize);
            reader = null;
            if (slice && slice.byteLength == sliceSize) {
                var view = new Uint8Array(slice);
                var arr = [];
                view.forEach(function(v) {
                    arr.push(v.toString(16));
                });
                view = null;
                var idx = arr.join(' ').indexOf(imgType);
                if (idx > -1) {
                    back(typeName[idx]);
                } else {
                    arr = arr.map(function(v) {
                        if (i > 3 && i < 8) {
                            return 'x';
                        }
                        return v;
                    });
                    var idx = arr.join(' ').indexOf(imgType);
                    if (idx > -1) {
                        back(typeName[idx]);
                    } else {
                        back(false);
                    }

                }
            } else {
                back(false);
            }

        });
    } else {
        var type = file.name.match(/\.(\w+)$/)[1];
        back(type);
    }
}

调用方法如下

checkFileType('(mov|mp4|avi)',file,function(fileType){
    // fileType = mp4,
    // 如果file的类型不在枚举之列,则返回false
});

上面上传文件的一步,可以改成:

formdata.append('filename', md5code+'.'+fileType);

有了切割上传后,也就有了文件唯一标识信息,断点续传变成了后台的一个小小的逻辑判断

后端主要做的内容为:根据前端传给后台的md5值,到服务器磁盘查找是否有之前未完成的文件合并信息(也就是未完成的半成品文件切片),取到之后根据上传切片的数量,返回数据告诉前端开始从第几节上传

如果想要暂停切片的上传,可以使用XMLHttpRequestabort方法

三、使用场景

  • 大文件加速上传:当文件大小超过预期大小时,使用分片上传可实现并行上传多个 Part, 以加快上传速度
  • 网络环境较差:建议使用分片上传。当出现上传失败的时候,仅需重传失败的Part
  • 流式上传:可以在需要上传的文件大小还不确定的情况下开始上传。这种场景在视频监控等行业应用中比较常见

小结

当前的伪代码,只是提供一个简单的思路,想要把事情做到极致,我们还需要考虑到更多场景,比如

  • 切片上传失败怎么办
  • 上传过程中刷新页面怎么办
  • 如何进行并行上传
  • 切片什么时候按数量切,什么时候按大小切
  • 如何结合 Web Worker 处理大文件上传
  • 如何实现秒传

人生又何尝不是如此,极致的人生体验有无限可能,越是后面才发现越是精彩 _

参考文献

  • https://segmentfault.com/a/1190000009448892
  • https://baike.baidu.com/

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

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

相关文章

标题:Vue3 中父组件向子组件通信的方式

标题&#xff1a;Vue3 中父组件向子组件通信的方式 在 Vue3 中&#xff0c;父组件和子组件之间可以通过一些方式进行通信。其中&#xff0c;父组件向子组件通信主要有两种方式&#xff1a;传值和调用子组件的方法。 一、父组件向子组件传值 当父组件需要向子组件传递数据时&a…

快速排序---算法

1、算法概念 快速排序&#xff1a;通过一趟排序将待排记录分隔成独立的两部分&#xff0c;其中一部分记录的数据均比另一部分的数据小&#xff0c;则可分别对这两部分记录继续进行排序&#xff0c;以达到震哥哥序列有序。 快速排序的最坏运行情况是O()&#xff0c;比如说顺序数…

设计一个动物声音“模拟器”,希望模拟器可以模拟许多动物的叫声。

设计一个动物声音“模拟器”&#xff0c;希望模拟器可以模拟许多动物的叫声。要求如下&#xff1a; &#xff08;1&#xff09;编写接口Animal Animal接口有2个抽象方法cry()和getAnimaName()&#xff0c;即要求实现该接口的各种具体动物类给出自己的叫声和种类名称。 &…

设计模式 - 中介器模式

中介者模式使得组件通过一个中心点——中介者进行交互。组件不需要直接进行通信&#xff0c;而是将请求发送给中介者&#xff0c;由中介者进行转发&#xff01;在JavaScript中&#xff0c;中介者往往只是一个对象字面量或一个函数。 你可以将这种模式与空中交通管制员和飞行员…

科技革新,OTG充电新纪元!

在科技日新月异的今天&#xff0c;数据交互已经渗透到我们生活的每个角落&#xff0c;无论是工作还是娱乐&#xff0c;它都发挥着不可替代的作用。OTG技术的出现&#xff0c;极大地简化了设备间的联接与数据交换过程&#xff0c;但随之而来的接口有限和续航问题&#xff0c;也让…

产品经理的进阶之路

点击下载《产品经理的进阶之路》 1. 前言 本文深入剖析了产品经理这一职业从产品专员起步,逐步晋升为产品经理、高级产品经理,直至产品总监的整个职业发展路径。在每个阶段,产品经理都需承担不同的工作职责,展现出独特的职业特点。 2. 产品专员 关键词【产品需求/原型/文…

计算机服务器中了rmallox勒索病毒怎么办?rmallox勒索病毒解密数据恢复

网络技术的不断发展与应用&#xff0c;大大提高了企业的生产运营效率&#xff0c;越来越多的企业开始网络开展各项工作业务&#xff0c;网络在为人们提供便利的同时&#xff0c;也会存在潜在威胁。近日&#xff0c;云天数据恢复中心接到多家企业的求助&#xff0c;企业的计算机…

设计模式-概述篇

1. 掌握设计模式的层次 第1层&#xff1a;刚开始学编程不久&#xff0c;听说过什么是设计模式第2层&#xff1a;有很长时间的编程经验&#xff0c;自己写了很多代码&#xff0c;其中用到了设计模式&#xff0c;但是自己却不知道第3层&#xff1a;学习过了设计模式&#xff0c;…

Vue 3.0生命周期:深入理解与用法

Vue 3.0生命周期&#xff1a;深入理解与用法 摘要&#xff1a; 本文将深入探讨Vue 3.0的生命周期&#xff0c;解释每个生命周期钩子的含义和用法&#xff0c;以及它们在开发过程中的重要性。我们将详细解析每个钩子的工作原理&#xff0c;并提供一些示例和最佳实践&#xff0c…

python爬取B站视频

参考&#xff1a;https://cloud.tencent.com/developer/article/1768680 参考的代码有点问题&#xff0c;请求头需要修改&#xff0c;上代码&#xff1a; import requests import re # 正则表达式 import pprint import json from moviepy.editor import AudioFileClip, Vid…

常见贪心问题详解

目录 贪心算法应用条件 常见贪心问题 活动安排问题&#xff08;区间调度问题&#xff09; 区间覆盖问题 最优装载问题1 最优装载问题2 多机调度问题 例题&#xff1a;翻硬币 例题&#xff1a;快乐司机 例题&#xff1a;防御力 例题&#xff1a;答疑 贪心算法应用条件…

【通信原理笔记】【三】模拟信号调制——3.1 模拟信号调制基本模型与思路

文章目录 前言一、模拟信号二、模拟调制系统模型三、模拟调制的三种方式四、调制的评价指标总结 前言 一般常见的信号的频带均集中在基带附近&#xff0c;如果要通过无线地方式传输&#xff0c;其较长的波长需要大型的天线才能传输&#xff0c;难以实现。另一方面基带的带宽资…

ruoyi-nbcio-plus基于vue3的flowable执行监听器的升级修改

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…

Linux基础命令篇之——压缩与解压(tar、gzip、bzip2、zip和unzip)

linux基础命令——解压与压缩 以下是关于Linux命令tar、gzip、bzip2、zip和unzip的详细介绍&#xff1a; 1. tar 这个是Linux用的最多的解压缩命令 tar是Linux系统中用于创建和处理归档文件的命令。归档文件是一个包含多个文件和/或目录的单一文件。常与压缩命令gzip或bzip2结…

图像处理_积分图

目录 1. 积分图算法介绍 2. 基本原理 2.1 构建积分图 2.2 使用积分图 3. 举个例子 1. 积分图算法介绍 积分图算法是图像处理中的经典算法之一&#xff0c;由Crow在1984年首次提出&#xff0c;它是为了在多尺度透视投影中提高渲染速度。 积分图算法是一种快速计算图像区域和…

wavedec2函数及使用

在MATLAB中&#xff0c;进行小波分解及其逆运算是处理图像的一种常见方法&#xff0c;尤其适用于图像分析、压缩和去噪等场景。wavedec2函数可以对二维信号&#xff08;例如图像&#xff09;进行多级小波分解&#xff0c;而waverec2函数则用于进行相应的逆运算。以下是如何使用…

非NVIDIA平台下的CUDA的替代方案OpenCL,第一步如何获取PlatformInfo、DeviceInfo

非NVIDIA平台下的CUDA的替代方案OpenCL&#xff0c;第一步如何获取PlatformInfo、DeviceInfo 介绍 当谈到高性能计算&#xff0c;NVIDIA的CUDA框架无疑是一个强大的工具。OpenC&#xff08;Open Computing Language&#xff09;是一个更为通用的解决方案&#xff0c;或者你使用…

java 跳转搜索(Jump Search)

与二分搜索一样&#xff0c;跳转搜索是一种针对排序数组的搜索算法。基本思想是通过按固定步骤向前跳跃或跳过某些元素来代替搜索所有元素来检查更少的元素&#xff08;比线性搜索&#xff09;。例如&#xff0c;假设我们有一个大小为 n 的数组 arr[] 和一个大小为 m 的块&…

隐私计算实训营学习六:隐语PIR介绍及开发指南

文章目录 一、隐语实现的PIR总体介绍1.1 PIR的定义和种类1.2 隐语PIR功能分层 二、Index PIR-SealPIR介绍三、Keyword PIR- Labeled PSI介绍四、隐语PIR后续计划 一、隐语实现的PIR总体介绍 1.1 PIR的定义和种类 PIR(Private Information Retrieval PIR)隐匿查询&#xff1a;…

比亚迪的薪资。。。当年的迪子,今年的迪爹……

大家好&#xff0c;我是小黄。 俗话说金3银4&#xff0c;比亚迪一直都是一个相对热门的公司&#xff0c;“去年迪子&#xff0c;今年迪爹&#xff0c;去年座位空一大半&#xff0c;今年走廊都站满人……”这是很多应届毕业生对比亚迪的评价。 当年小黄毕业的时候&#xff0c;…