iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >JavaScript利用切片实现大文件断点续传
  • 519
分享到

JavaScript利用切片实现大文件断点续传

2024-04-02 19:04:59 519人浏览 八月长安
摘要

目录什么是断点续传实现思路需要后端提供的api获取已经上传的切片上传切片合并切片前端代码细节实现HASH值的获取方法切片处理总体html结构使用axiOS发送请求整体逻辑和代码实现效

最近公司需要用户上传比较大的文件,一个文件可能大于1GB,如果出现网络波动或者用户违规操作导致上传中断,那么就必须重新重头上传。身为前端,与后端商量后,查看了一些已经成熟的的实现方案,最后使用断点续传优化上传的逻辑。

完整代码-GitHub

什么是断点续传

在文件上传期间因为一些原因而导致上传终止(刷新或网络中断)时,下次再次上传同一个文件就从上一次上传到一半的地方继续上传,以节省上传时间。

实现思路

主体思路是将比较大的文件分成若干个切片。并非一次性将一个文件整体传输给服务器,而是将分割的切片一部分一部分地传给服务器。服务器将已上传的切片暂存,当所有的切片都上传成功了,再将切片合并成一整个文件。

这么做就可以利用切片使用断点续传的功能。具体逻辑是如果在上传切片的途中断了,那么下次再次上传同一个文件的时候可以先向服务器端发一个请求,获取已经上传了哪些切片,与整体切片进行比对后,再将剩下未上传的切片继续上传。

其中后端处理切片和合并的一个细节点为:将大文件进行切片时,后端需要将每一个切片的文件名为${原文件HASH值}_${切片序号}.${文件后缀}例如'f07ec272dbb0b883eed4b2f415625a90_2.mp4'。并且将服务器存的切片的临时文件夹的名字命名为hash值。最后所有的切片上传完成时,再调用合并接口,后端将所有临时文件夹中的切片合并。

需要后端提供的api

以下api需要后端提供开发

获取已经上传的切片

  url:/upload_already
  method:GET
  params:
    HASH:文件的HASH名字
  return:application/JSON
    code:0成功 1失败,
    codeText:状态描述,
    fileList:[...]

此方法用来获取已上传文件的所有切片的切片名,例如返回:

{
fileList:['f07ec272dbb0b883eed4b2f415625a90_1.mp4','f07ec272dbb0b883eed4b2f415625a90_2.mp4','f07ec272dbb0b883eed4b2f415625a90_3.mp4']
}

意思为HASH值为'f07ec272dbb0b883eed4b2f415625a9'的文件已经上传了三个切片,接下来只需要从第四个切片开始上传即可。如果是空数组,说明此文件第一次上传。

上传切片

  url:/upload_chunk
  method:POST
  params:multipart/fORM-data
    file:切片数据
    filename:切片名字「文件生成的HASH_切片编号.后缀」
  return:application/json
    code:0成功 1失败,
    codeText:状态描述,
    originalFilename:文件原始名称,
    servicePath:文件服务器地址

上传file对象格式的切片文件,并且将切片名字已${原文件HASH值}_${切片序号}.${文件后缀}格式传给后端,后端利用hash值将切片暂存在一个临时文件夹中,最后所有切片上传完成,就将切片合并,然后删除这个临时文件夹。

合并切片

  url:/upload_merge
  method:POST
  params:application/x-www-form-urlencoded
    HASH:文件的HASH名字
    count:切片数量
  return:application/json
    code:0成功 1失败,
    codeText:状态描述,
    originalFilename:文件原始名称,
    servicePath:文件服务器地址

当所有切片上传完成(前段自行判断)之后,调用合并接口,后端会将切片合并,然后删除存切片的临时文件夹。

前端代码细节实现

HASH值的获取方法

  • 使用FileReader对象将选择的文件对象转为buffer
  • 依据文件的buffer使用MD5库生成文件的HASH值。

封装成函数:


const changeBuffer = file => {
    return new Promise(resolve => {
        let fileReader = new FileReader();
        fileReader.readAsArrayBuffer(file);
        fileReader.onload = ev => {
            let buffer = ev.target.result,
                spark = new SparkMD5.ArrayBuffer(),
                HASH,
                suffix;
            spark.append(buffer);
            HASH = spark.end();
            suffix = /.([a-zA-Z0-9]+)$/.exec(file.name)[1];
            resolve({
                buffer,
                HASH,
                suffix,
                filename: `${HASH}.${suffix}`
            });
        };
    });
};

切片处理

使用Blob对象中的slice函数可以进行对文件进行切片处理。可以将文件切为自定义的大小和数量。

Blob.slice文档参考

例如file.slice(0,1024)代表将文件的0-1024字节数据切片,然后返回一个新的对象。

总体html结构

<div class="container">
    <div class="item">
        <h3>大文件上传</h3>
        <section class="upload_box" id="upload7">
            <input type="file" class="upload_inp">
            <div class="upload_button_box">
                <button class="upload_button select">上传文件</button>
            </div>
            <div class="upload_progress">
                <div class="value"></div>
            </div>
        </section>
    </div>
</div>

使用axios发送请求


//创建一个单独的实例,不去项目全局的或者其他的axios冲突
let instance = axios.create();
instance.defaults.baseURL = 'Http://127.0.0.1:8888';
//默认是multipart/form-data格式
instance.defaults.headers['Content-Type'] = 'multipart/form-data';
instance.defaults.transformRequest = (data, headers) => {
    //兼容x-www-form-urlencoded格式的请求发送
    const contentType = headers['Content-Type'];
    if (contentType === "application/x-www-form-urlencoded") return Qs.stringify(data);
    return data;
};
//统一结果的处理
instance.interceptors.response.use(response => {
    return response.data;
},reason=>{
    //统一失败的处理
    return Promise.reject(reason)
});

整体逻辑和代码

详细的逻辑在注释当中,写的比较详细

完整代码-github

(function () {
    let upload = document.querySelector('#upload7'),
        upload_inp = upload.querySelector('.upload_inp'),
        upload_button_select = upload.querySelector('.upload_button.select'),
        upload_progress = upload.querySelector('.upload_progress'),
        upload_progress_value = upload_progress.querySelector('.value');

    const checkIsDisable = element => {
        let classList = element.classList;
        return classList.contains('disable') || classList.contains('loading');
    };

    
    const changeBuffer = file => {
        return new Promise(resolve => {
            let fileReader = new FileReader();
            fileReader.readAsArrayBuffer(file);
            fileReader.onload = ev => {
                let buffer = ev.target.result,
                    spark = new SparkMD5.ArrayBuffer(),
                    HASH,
                    suffix;
                spark.append(buffer);
                HASH = spark.end();
                suffix = /.([a-zA-Z0-9]+)$/.exec(file.name)[1];
                resolve({
                    buffer,
                    HASH,
                    suffix,
                    filename: `${HASH}.${suffix}`
                });
            };
        });
    };

    upload_inp.addEventListener('change', async function () {
        //get native file object
        let file = upload_inp.files[0];
        if (!file) return;
        //button add loading
        upload_button_select.classList.add('loading');
        //show progress
        upload_progress.style.display = 'block';

        // 获取文件的HASH
        let already = [],//已经上传过的切片的切片名
            data = null,
            {
                HASH,
                suffix
            } = await changeBuffer(file);//得到原始文件的hash和后缀

        // 获取已经上传的切片信息
        try {
            data = await instance.get('/upload_already', {
                params: {
                    HASH
                }
            });
            if (+data.code === 0) {
                already = data.fileList;
            }
        } catch (err) {}

        // 实现文件切片处理 「固定数量 & 固定大小」
        let max = 1024 * 100,//切片大小先设置100KB
            count = Math.ceil(file.size / max),//得到应该上传的切片
            index = 0,//存放切片数组的时候遍历使用
            chunks = [];//用以存放切片值
        if (count > 100) {//如果切片数量超过100,那么就只切成100个,因为切片太多的话也会影响调用接口的速度
            max = file.size / 100;
            count = 100;
        }
        while (index < count) {//循环生成切片
            //index 0 =>  0~max
            //index 1 =>  max~max*2
            //index*max ~(index+1)*max
            chunks.push({
                file: file.slice(index * max, (index + 1) * max),
                filename: `${HASH}_${index+1}.${suffix}`
            });
            index++;
        }

        index = 0;
        const clear = () => {//上传完成后,将状态回归
            upload_button_select.classList.remove('loading');
            upload_progress.style.display = 'none';
            upload_progress_value.style.width = '0%';
        };

        //每一次上传一个切片成功的处理[进度管控&切片合并]
        const complate = async () => {
            // 管控进度条:每上传完一个切片,就将进度条长度增加一点
            index++;
            upload_progress_value.style.width = `${index/count*100}%`;

            if (index < count) return;
            // 当所有切片都上传成功,就合并切片
            upload_progress_value.style.width = `100%`;
            try {
                //调用合并切片方法
                data = await instance.post('/upload_merge', {
                    HASH,
                    count
                }, {
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded'
                    }
                });
                if (+data.code === 0) {
                    alert(`恭喜您,文件上传成功,您可以基于 ${data.servicePath} 访问该文件~~`);
                    clear();
                    return;
                }
                throw data.codeText;
            } catch (err) {
                alert('切片合并失败,请您稍后再试~~');
                clear();
            }
        };

        // 循环上传每一个切片
        chunks.forEach(chunk => {
            // 已经上传的无需在上传
            //后台返回的already格式为['HASH_1.png','HASH_2.png'],既已经上传的文件的切片名
            if (already.length > 0 && already.includes(chunk.filename)) {
                //已经上传过了的切片就无需再调用接口上传了
                complate();//动进度条或合并所有切片
                return;
            }
            let fm = new FormData;
            fm.append('file', chunk.file);
            fm.append('filename', chunk.filename);
            instance.post('/upload_chunk', fm).then(data => {//使用form data格式上传切片
                if (+data.code === 0) {
                    complate();////动进度条或合并所有切片
                    return;
                }
                return Promise.reject(data.codeText);
            }).catch(() => {
                alert('当前切片上传失败,请您稍后再试~~');
                clear();
            });
        });
    });
    //触发原生的上传文件框
    upload_button_select.addEventListener('click', function () {
        if (checkIsDisable(this)) return;
        upload_inp.click();
    });
})();

实现效果

第一次上传时,分别调用/upload_already,/upload_chunk方法获取已上传的切片(空数组),然后进行切片分割,再一个个进行上传。

当在此时刷新页面,终端切片的上传行为时。此时我们看一下后端的临时数据

可以看到一个以hash至命名的临时文件夹,并且已经上传了24个切片

如果我们再次选择同样的文件上传,进度条会立即到上次已经上传的位置,already接口返回已上传的24个切片的名字的数组。

将剩下的切片上传完成之后,会调用merge接口。完成上传

此时后端的临时文件夹被删除,合并为一整个文件。上传结束

到此这篇关于javascript利用切片实现大文件断点续传的文章就介绍到这了,更多相关JavaScript文件断点续传内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: JavaScript利用切片实现大文件断点续传

本文链接: https://www.lsjlt.com/news/144479.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JavaScript利用切片实现大文件断点续传
    目录什么是断点续传实现思路需要后端提供的api获取已经上传的切片上传切片合并切片前端代码细节实现HASH值的获取方法切片处理总体html结构使用axios发送请求整体逻辑和代码实现效...
    99+
    2022-11-13
  • java如何实现文件切片上传服务器+断点续传
    这篇文章主要为大家展示了“java如何实现文件切片上传服务器+断点续传”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“java如何实现文件切片上传服务器+断点续传”这篇文章吧。1.定义一个实体类用...
    99+
    2023-06-22
  • springboot大文件上传、分片上传、断点续传、秒传的实现
    对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达...
    99+
    2022-11-13
  • js自己实现一个大文件切片上传+断点续传的示例代码
    目录首先我们来分析一下需求一、 格式校验二、 文件切片三、 断点续传 + 秒传 + 上传进度PM:喂,那个切图仔,我这里有个100G的视频要上传,你帮我做一个上传后台,下班前给我哦,...
    99+
    2022-11-13
  • java实现文件切片上传百度云+断点续传的方法
    前言: 本文代码通过dubbo进行远程调用的接口,如果不使用dubbo,直接将service放到你的service,并稍作修改,redis替换成自己封装的工具即可。下方代码有点多,但...
    99+
    2022-11-12
  • React+Node实现大文件分片上传、断点续传秒传思路
    目录1、整体思路2、实现步骤2.1 文件切片加密2.2 查询上传文件状态2.3 秒传2.4 上传分片、断点续传2.5 合成分片还原完整文件3、总结4、后续扩展与思考5、源码1、整体思...
    99+
    2022-11-13
  • vue怎么实现大文件分片上传与断点续传送
    本文小编为大家详细介绍“vue怎么实现大文件分片上传与断点续传送”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue怎么实现大文件分片上传与断点续传送”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。问题:前段时间...
    99+
    2023-07-02
  • Vue 大文件上传和断点续传的实现
    目录文件上传的 2 套方案基于文件流(form-data)客户端把文件转换为 base64大文件上传获取到文件对象并转成 ArrayBuffer 对象创建切片发送请求所有切片发送成功...
    99+
    2022-11-12
  • Vue+Node实现大文件上传和断点续传
    目录源代码Blob.slice切片初始化文件内容FormData.append()大文件上传断点续传代码创建切片源码worker 线程通讯的逻辑断点续传秒传源代码 断点续传、分片上传...
    99+
    2022-11-13
  • vue实现大文件分片上传与断点续传到七牛云
    问题: 前段时间做视频上传业务,通过网页上传视频到服务器。 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题: 1、文件过大,超出服务端的请求...
    99+
    2022-11-13
  • Html5大文件断点续传的实现方法
    本篇内容介绍了“Html5大文件断点续传的实现方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2022-10-19
  • vue 大文件分片上传(断点续传、并发上传、秒传)
    对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达...
    99+
    2022-11-13
  • Node.js实现大文件断点续传示例详解
    目录前言方案分析具体解决流程html 部分script 部分node服务端 部分逻辑分析小结前言 平常业务需求:上传图片、Excel等,毕竟几M的大小可以很快就上传到服务器。 针对于...
    99+
    2022-11-13
    Node.js大文件断点续传 Node.js 文件断点续传
  • Vue+Node怎么实现大文件上传和断点续传
    本篇内容介绍了“Vue+Node怎么实现大文件上传和断点续传”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!源代码断点续传、分片上传、秒传、重...
    99+
    2023-06-30
  • java实现文件的断点续传
    所谓文件的断点续传,就是一个线程传输文件,另一个线程控制传输标识,以达到暂停文件效果、恢复文件上传的效果。 本demo使用最基本的线程之间的通信来实现一个简单的断点续传。 packa...
    99+
    2022-11-13
  • Vue实现大文件分片上传,包括断点续传以及上传进度条
    首先解释一下什么是分片上传         分片上传就是把一个大的文件分成若干块,一块一块的传输。这样做的好处可以减少重新上传的开销。比如:如果我们上传的文件是一个很大的文件,那么上传的时间应该会比较久,再加上网络不稳定各种因素的影响,很容...
    99+
    2023-09-27
    vue.js 前端 javascript
  • 如何使用大文件上传:秒传、断点续传、分片上传方法
    本篇内容介绍了“如何使用大文件上传:秒传、断点续传、分片上传方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!秒传1、什么是秒传通俗的说,你...
    99+
    2023-06-15
  • Vue在大文件上传和断点续传的实现方法
    本篇内容主要讲解“Vue在大文件上传和断点续传的实现方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue在大文件上传和断点续传的实现方法”吧!文件上传的 2 套方案基于文件流(form-da...
    99+
    2023-06-20
  • Python 工具类实现大文件断点续传功能详解
    依赖 os、sys、requests 工具代码 废话不多说,上代码。 #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Cre...
    99+
    2022-11-12
  • 通过Java实现文件断点续传功能
    目录什么是断点续传解决方案效果演示参考代码前端后端什么是断点续传 用户上传大文件,网络差点的需要历时数小时,万一线路中断,不具备断点续传的服务器就只能从头重传,而断点续传就是,允许用...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作