Springboot+WebUploader优雅实现超大文件的分片上传(一)

这篇具有很好参考价值的文章主要介绍了Springboot+WebUploader优雅实现超大文件的分片上传(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

webuploader,Springboot实战与原理,java,spring boot,webuploader,超大文件上传,文件分片,分片上传

前言

webuploader,Springboot实战与原理,java,spring boot,webuploader,超大文件上传,文件分片,分片上传

在软件工程里,在处理“大”的时候一直是一个痛点和难点,如并发大、数据量大、文件大,对硬件进行升级可以解决一些问题,但这并不最聪明的办法,而对于老板来说,这也不是成本最小的办法。作为开发人员来说,在面对类似极端的问题时,只可智取,不可硬刚,最大化利用好手上现有的资源,以更加优雅的办法来满足用户多样化的需求才是王道。今天的主题也是一个“大”的问题,就是超大文件如何上传和下载?其实在解决这个问题之前,有一个问题是绕不过去的:什么才是大文件?什么是超大文件?几十兆?几百兆?还是几个G?这其实是一个极具争议的问题,在不同的业务场景下,对于大文件的“大”的理解和定义可能是不同的。但这并不是本文的重点,我想和大家分享的是,面对所谓的大文件时,如何优雅实现上传。

不管多简单的需求,在量级达到一定程度时,就会变得异常复杂。所以这篇文章会有点长,有两部分组成,如果觉得一步一步看太累,想直接看结果,文章的最后面附上了前后端的两个关键文件,可以直接拿到编辑里边调试边一步一步看:

Springboot+WebUploader优雅实现大文件的分片上传(一)

Springboot+WebUploader优雅实现大文件的分片上传(二)

文章示例环境配置信息
jdk版本:1.8
开发工具:Intellij iDEA 2020.1
springboot:2.3.9.RELEASE

目标

实现超大文件的上传和断点续传。

实现思路

对于各种“大”的问题,很多的场景采用是分而治之的理念进行设计,而对于大文件上传来说,从这一理念出发,具体的方法就是把大文件分片上传,分片全部上传后再对分片进行合并。那什么是大文件分片呢?什么又是合并分片呢?如果分片上传过程中,个别分片上传失败了,需要重新分片上传吗?其实文件上传本身是一个很简单的需求,当量级达到一定程度时,就变得异常复杂了,需要考虑的问题点很多,同理其他“大”的问题也是类似的。

大文件分片

大文件分片是将要上传的大文件,按照一定的规则,将整个文件进行切片,即把一个大的数据文件切分成小的数据块,然后再按照一定的策略(串行或并发)进行上传;

webuploader,Springboot实战与原理,java,spring boot,webuploader,超大文件上传,文件分片,分片上传

合并分片

大文件分片合并是大文件分片上传的后续操作,当一个大文件被切分成小的数据块上传到服务器时,需要把这些小的数据块按照原来的顺序再进行合并,还原成原来的大文件,这就是合并分片;

文件分片和分片合并其实很好理解,类似于现实生活中,如果想把一架大飞机藏在洞口比较小的山洞里,要怎么做呢?肯定是先把飞机拆成小的零件,然后运输到山洞里面,再根据飞机的装配图纸把所有的零件再装好。原理就这么简单。

webuploader,Springboot实战与原理,java,spring boot,webuploader,超大文件上传,文件分片,分片上传

断点续传

大文件分片上传的时候,个别分片有可能会碰到网络故障,而导致一些分片文件上传失败,如果网络恢复后,可以从上传失败的分片开始上传而直接跳过上传成功的分片文件部分,那就可以节约时间,提高上传效率,这就是断点续传;如下图:一个大文件被切分成了n个分片,分片上传过程中,分片2和分片3因为某种原因上传失败了,再次上传的时候,会直接跳过所有上传成功的分片,直接从失败的分片2和分片3开始上传,合并分片的时候检测到所有的分片都完成了上传,就可以合并还原成大文件本身的面目了;

webuploader,Springboot实战与原理,java,spring boot,webuploader,超大文件上传,文件分片,分片上传

实现原理

实现思路很简单,实现原理其实也很简单,具体如下:

1、前端使用百度的WebUploader组件,选中待上传的大文件,然后计算出文件的md5值;

2、WebUploader组件开启分片上传后,选中的待上传文件会按照配置好的分片规则进行分片,分片文件上传前会计算出分片文件的md5值;

3、通过webuploader组件api计算出分片的md5值后,会携带文件md5值和分片文件的md5值,调用后台接口检验当前分片是否已经上传过;若上传过,则直接跳过;若未上传过,则开始分片上传;

4、前端往后端传输分片文件的过程可以是并发执行,这里一定注意传递到后台的分片并不一定是按照分片的顺序来的,后端收到分片文件后,会保存分片文件到硬盘、网盘等存储介质上,同时也要保存分片文件md5值、文件md5值等分片参数信息;

5、待所有的分片上传成功后,会触发WebUploader的uploadSuccess事件,然后在这里再发起合并分片请求;

6、后端收到合并分片的请求后,再次检查所有的分片是否上传完整,若上传完整,则开始合并所有分片;

7、这里要特别注意,合并分片的时候,一定要按钮分片时的切割顺序来合并,否则文件就会打不开或运行不了;合并完所有分片文件后,分片文件就没有用了,可以删除了;

md5消息摘要算法,属Hash算法一类,主要特点是不可逆,相同数据的md5值肯定一样,不同数据的md5值不一样;对于数据文件,不管文件名字是否相同,如果数据文件内容相同,则文件的md值是相同的;webuploader组件提供了文件的md5值的计算方法,其计算过程是异步的;
webuploader,Springboot实战与原理,java,spring boot,webuploader,超大文件上传,文件分片,分片上传

代码实现

大文件分片上传的实现原理,逻辑比较清晰,那么落地到代码实现上,还有几个问题需要解决:

1、webuploader组件中,分片上传怎么开启?

2、webuploader组件中,文件的md5值如何计算?

3、webuploader组件中,分片文件的md5值如何计算?

4、webuploader组件中,分片上传的的请求在哪里触发?

5、前端、后端如何校验分片是否已经上传?

6、后端如何处理分片上传请求?

7、webuploader组件中,合并文件分片的请求在哪里触发?

8、后端如何合并分片请求?

9、分片上传失败后,如何在断点处继续上传?

10、上传的进度条是怎么实现的?

1、webuploader组件中,分片上传怎么开启?

webuploader的分片上传开启实际上很简单,在创建webuploader对象时,设置chunked为true,即表示开启分片上传;chunkSize可以设置分片大小,即以多大的体积进行分片;chunkRetry可以设置重传次数,有的时候由于网络原因,分片上传的会失败,这里即是失败允许重的次数;threads可以设置允许最大由几个进程发起上传请求;


uploader = WebUploader.create({
    // swf文件路径
    swf: 'http://localhost:8080/lib/Uploader.swf',
    // 分片文件上传接口
    server: 'http://localhost:8080/file/upload',
    // 选择文件的按钮。可选。
    pick: '#picker',
    fileVal: 'multipartFile',//后端用来接收上传文件的参数名称
    chunked: true,//开启分片上传
    chunkSize: 1024 * 1024 * 10,//设置分片大小
    chunkRetry: 2,//设置重传次数,有的时候由于网络原因,分片上传的会失败,这里即是失败允许重的次数
    threads: 3//允许同时最大上传进程数
});

2、webuploader组件中,文件的md5值如何计算?

文件的md5计算可以引用spark-md5.js,据传言是javascript里md5加密计算速度最快的,当然在webuploader.js里也有具体的api可以使用;引入webuploader.js后,调用 WebUploader.Uploader.md5File(...)即可计算文件的md5值,这里需要注意的是md5File(...),有三个参数,分别是file,数据起始位置、数据结束位置,返回的是一个promise对象,要想拿到具体的值还要再调用then(function(val){}),具体步骤如下:

1、当添加完文件后,webuploader的fileQueued事件被触发;

2、fileQueued事件触发后,在回调函数里计算出文件的的md5值,这里注意是要计算出整个文件的md5值,而不是一部分,关键就在md5File(...)方法的后两个参数数据起始位置和数据结束位置,看到很多人实际上是用错了,只计算了文件一部分的md5值,并不是整个文件的md5值;md5的计算过程是异步操作,并且文件越大,计算用时就越长;

3、这里还用到了deferred,deferred的作用就是监控异步计算文件md5值这个异步操作的执行状态;文件md5值计算完成后,更新状态为已完成,这时 deferred.done()会触发;更新md5计算标志位为true,这时再点击开始上传按钮时,就会再有弹窗提示:md5计算中...,请稍侯;

webuploader内部有很多种command,其中有一个叫before-send-file,也可以在文件上传前会触发,此时还没有开始分片,可以用来做文件整体的md5计算;需要注意的是before-send-file的触发时机是要晚于fileQueued事件的;before-send-file是在点击开始上传按钮执行uploader.upload()后才会触发;而fileQueued事件是在选择文件后,立刻触发,不用等到点击开始上传按钮;所以具体在哪里进行计算,可根据实际业务酌情选择;

/**
 * 当有文件被添加进队列后触发
 * 主要逻辑:1、文件被添加到队列后,开始计算文件的md5值;
 * 2、md5的计算过程是异步操作,并且文件越大,计算用时越长;
 * 3、变量md5FlagMap是文件md5值计算的标志位,计算完成后,设置当前文件的md5Flag为true
 */
 //md5FlagMap用于存储文件md5计算完成的标志位;多个文件时,分别设置标志位,key是文件名,value是true或false;
var md5FlagMap = new Map();
uploader.on('fileQueued', function (file) {
    md5FlagMap.set(file.name, false);//文件md5值计算的标志位默认为false
    var deferred = WebUploader.Deferred();//deferred用于监控异步计算文件md5值这个异步操作的执行状态
    uploader.md5File(file, 0, file.size - 1).then(function (fileMd5) {
        file.wholeMd5 = fileMd5;
        file_md5 = fileMd5;
        deferred.resolve(file.name);//文件md5值计算完成后,更新状态为已完成,这时 deferred.done()会触发
    })
    //文件越大,文件的md5值计算用时越长,因此md5的计算搞成异步执行是合理的;如果异步执行比较慢的话,会顺序执行到这里
    $('#thelist').append('<div id="' + file.id + '" class="item">' +
        '<h4 class="info">' + file.name + '</h4>' +
        '<p class="state">开始计算大文件的md5......<br/></p>' +
        '</div>')
    //文件的md5计算完成,会触发这里的回调函数,
    deferred.done(function (name) {
        md5FlagMap.set(name, true);//更新md5计算标志位为true
        $('#' + file.id).find('p.state').append('大文件的md5计算完成<br/>');
    })
    return deferred.promise();
})

3、webuploader组件中,分片文件的md5值如何计算?

webuploader对象中配置好相关的开启分片设置参数后,当有文件被选中添加后,webuploader会帮你对文件按配置参数进行分片;在分片文件发送到后台之前,webuploader内部另一个command(before-send)会触发,before-send的触发时机在分片上传之前,可以用作在分片发送到后端之前计算出分片文件的md5,调用后台接口做分片是否已经上传的验证:如果分片已经上传成功了,直接跳过,不会再调用分片的上传接口;如果分片未上传,则会把分片的md5值赋给分片block上,webuploader的另一个事件‘uploadBeforeSend’在触发的时候,其回调函数的第一个参数中就可以拿到分片的md5值,然后传递到后台,如果下次再上传时分片时,则可以用于是否已经上传的校验;


WebUploader.Uploader.register({
    "add-file": "addFile",
    "before-send-file": "beforeSendFile",
    "before-send": "beforeSend",
    "after-send-file": "afterSendFile"
}, {
    addFile: function (file) {
        console.log('1', file)
    },
    beforeSendFile: function (file) {
        console.log('2', file)
    },
    beforeSend: function (block) {
        console.log(3)
        var file = block.file;
        var deferred = WebUploader.Base.Deferred();
        (new WebUploader.Uploader()).md5File(file, block.start, block.end).then(function (value) {
            $.ajax({
                url: 'http://localhost:8080/file/check',//检查当前分片是否已经上传
                method: 'post',
                data: {chunkMd5: value, fileMd5: file_md5,chunk:block.chunk},
                success: function (res) {
                    if (res) {
                        deferred.reject();
                    } else {
                        deferred.resolve(value);
                    }
                }
            });
        })
        deferred.done(function (value) {
            console.log('分片md5:', value)
            block.chunkMd5 = value;
        })
        return deferred;
    },
    afterSendFile: function (file) {
        console.log('4', file)
    }
})

4、webuploader组件中,分片上传的的请求在哪里触发?

当选择文件后,就开始计算整体文件的md5值了,未计算完成前,点击开始上传按钮,会直接弹出“md5计算中...,请稍侯”;考虑到多文件上传的情况,这里使用了map对象md5FlagMap来存储每个文件的md5是否计算完成标志,key是文件名称,value是true或false,表示文件md5文件是否计算完成;

如果不想用按钮来触发上传,WebUploader有一个参数是auto,默认是false,可以改为true,选中文件后自动开始上传;


//开始上传按钮被点击时触发
$('#ctlBtn').click(function () {
    //md5FlagMap存储有文件md5计算的标志位;
    // 同时上传多个文件时,上传前要判断一下已添加文件的md5是否计算完成,
    // 如果有未计算完成的,则继续等待计算结果;
    //文件上传标志位,如果多个文件有一个没有完成md5计算则不能开始上传;
    //这里在实际业务中可以更换成其他交互样式,酌情优化为哪个文件的md5计算完成,则开始哪个文件的上传;
    var uploadFloag = true;
    md5FlagMap.forEach(function (value, key) {
        if (!value) {
            uploadFloag = false;
            alert('md5计算中...,请稍侯')//文件md5计算未完成,会弹出弹窗提示;
        }
    })
    if (uploadFloag) {
        uploader.upload();//文件md5计算完成后,开始分片上传;
    }
})

添加的文件的md5计算完成后,再次点击开始上传按钮,调用 uploader.upload(),开始分片上传;但是在实际开始上传前,webuploader的command(before-send)和uploadBeforeSend事件会先后触发,这两个地方有一个共同作用就是在分片正式上传前可以再做点事情,根据官方文档介绍,不同的是before-send可以用作分片是否已上传的验证(详见第3个问题里);uploadBeforeSend事件的作用是在分片上传前添加一些附带参数到后端;


// 分片模式下,当文件的分块在发送前触发
uploader.on('uploadBeforeSend', function (block, data) {
    var file = block.file;
    //data可以携带参数到后端
    data.originalFilename = file.originalFilename;//文件名字
    data.md5Value = file.wholeMd5;//文件整体的md5值
    data.start = block.start;//分片数据块在整体文件的开始位置
    data.end = block.end;//分片数据块在整体文件的结束位置
    data.chunk = block.chunk;//分片的索引位置
    data.chunks = block.chunks;//整体文件总共分了多少征
    data.chunkMd5 = block.chunkMd5;//分片文件md5值
});

小结

Springboot+WebUploader优雅实现大文件的上传(一),主要讲述了大文件上传的实现思路、实现原理、以及主要的前端代码实现的。更多精彩的后端如何与前端协调配合内容,以及大文件分片上传的两个关键代码文件,请移步Springboot+WebUploader优雅实现大文件的上传(二)文章来源地址https://www.toymoban.com/news/detail-774986.html

到了这里,关于Springboot+WebUploader优雅实现超大文件的分片上传(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springboot实现minio文件分片上传

    在Spring Boot中实现MinIO的文件分片上传涉及到几个关键步骤。MinIO是一个高性能的分布式对象存储服务,它兼容Amazon S3的API。分片上传主要用于上传大文件,将大文件分成多个部分,分别上传,最后再将这些部分合并成一个完整的文件。这样做的好处是提高了上传的可靠性,并

    2024年01月17日
    浏览(59)
  • 使用SpringBoot优雅的实现文件上传

    文件上传,也称为upload,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。 服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件

    2024年02月11日
    浏览(43)
  • springboot整合Minio + vue 实现文件分片上传(完整代码)

    网上关于minio分片上传的资料不太详细,缺斤少两,所以我基于他们的代码做了一些修改,demo能够正常运行起来,但是偶尔也会发生一些小bug,不过这些都无伤大雅,最终目的是理解代码背后的逻辑和流程 流程: 前端获取生成文件MD5,发送至后台判断是否有该文件缓存,有

    2024年02月02日
    浏览(53)
  • 【SpringBoot整合系列】SpringBoot 实现大文件分片上传、断点续传及秒传

    小文件(图片、文档、视频)上传可以直接使用很多ui框架封装的上传组件,或者自己写一个input 上传,利用FormData 对象提交文件数据,后端使用spring提供的MultipartFile进行文件的接收,然后写入即可。 但是对于比较大的文件,比如上传2G左右的文件(http上传),就需要将文件

    2024年04月16日
    浏览(65)
  • 【java】java实现大文件的分片上传与下载(springboot+vue3)

    源码: https://gitee.com/gaode-8/big-file-upload 演示视频 https://www.bilibili.com/video/BV1CA411f7np/?vd_source=1fe29350b37642fa583f709b9ae44b35 对于超大文件上传我们可能遇到以下问题 • 大文件直接上传,占用过多内存,可能导致内存溢出甚至系统崩溃 • 受网络环境影响,可能导致传输中断,只能重

    2024年02月02日
    浏览(58)
  • 【万字长文】Vue+SpringBoot实现大文件秒传、断点续传和分片上传完整教程(提供Gitee源码)

    前言:最近在实际项目中碰到一个需求,客户可能会上传比较大的文件,如果采用传统的文件上传方案可能会存在服务器压力大、资源浪费甚至内存溢出的一些安全风险,所以为了解决一系列问题,需要采用新的技术方案来实现大文件的上传;空闲的时候参考了网上的一些相

    2024年02月12日
    浏览(44)
  • SpringBoot+前端文件分片上传

    在日常生活中,文件上传相关的操作随处可见,大到处理大数据量的文件,小到头像上传,都离不开文件上传操作,但是当一个文件的大小超过了某个阈值时,这个文件的上传过程就会变得及其的慢,且会消耗大量网络资源,这是我们不愿意看到的,所以,文件分片上传孕育而生。 文件分

    2024年02月19日
    浏览(36)
  • SpringBoot+vue文件上传&下载&预览&大文件分片上传&文件上传进度

    SpringBoot+vue 大文件分片下载 Blob File spark-md5根据文件内容生成hash 大文件分片上传(批量并发,手动上传)vue组件封装-form组件 vue上传大文件/视频前后端(java)代码 springboot+vue自定义上传图片及视频 SpringBoot + VUE实现前台上传文件获取实时进度( 使用commons-fileupload设置上传监听

    2024年02月05日
    浏览(74)
  • vue3 - 使用element-plus组件库el-upload上传超大mp4视频分片上传,Upload上传大文件mp4视频进行切片分段上传到后端服务器教程,vue3如何上传很大的视频(详细示例代码

    在vue3+elementPlus中,使用el-upload组件\\\"切片分段\\\"上传mp4大视频到服务器,支持任意大视频、大文档、大压缩包等超大文件,通用方法将其拆分成多个小段进行逐个逐条上传到后端(支持断点续传、下载预览)。 详细大文件分片功能源码,可只拿前端源码或只拿springboot(Java)后

    2024年03月16日
    浏览(75)
  • SpringBoot + minio实现分片上传、秒传、续传

    MinIO是一个基于Go实现的高性能、兼容S3协议的对象存储。它采用GNU AGPL v3开源协议,项目地址是https://github.com/minio/minio。 引用官网: MinIO是根据GNU Affero通用公共许可证v3.0发布的高性能对象存储。它与Amazon S3云存储服务兼容。使用MinIO构建用于机器学习,分析和应用程序数据工

    2024年02月08日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包