前后端实现大文件文段上传超级简单版

前后端实现分段上传简易教程

1.大概思路

废话不多说,简单思路如下:

  1. 前端将将file文件分片成多个blob文件,每个blob可以按默认最大上传大小设置。
  2. 将每个blob编写简单的顺序id,然后通过异步的方式传给后端。
  3. 每个blob都需要计算md5值,同时后端也需要计算这是保证分段数据不损坏的关键。
  4. 最后后端按id顺序合成新的二进制数组,写入到文件中。

2.前后端环境

前端环境如下:

  1. vue这个不重要,不用vue也可以实现
  2. spark-md5这个是计算blob的md5利器
  3. axios进行异步http请求

后端主要环境如下:

  1. hutool包,这个主要也是用来计算MD5值和写入文件

3.前端案例代码

//blob切片
sliceFile(file) {
            const maxsize = 1024 * 1024 //1M
            var length = file.size
            var segmentList = []
            let to = maxsize
            for (let start = 0; start < length; start = to, to += maxsize) {
                segmentList.push(file.slice(start, to))
            }
            return segmentList
},

//发送切片
sendBlob(blob, allcode, index, allindex) {
            var reader = new FileReader()
            var formdata = new FormData()
            formdata.append("file", blob)
            reader.onload = (e) => {
                var md5 = SparkMD5.hashBinary(e.target.result)
                formdata.append("segcode", md5)
                formdata.append("allcode", allcode)
                formdata.append("index", index)
                formdata.append("allindex", allindex)
                formdata.append("services", this.selectedServiceList)
                axios.post("http://localhost/audio/filesegment/upload", formdata).then(e => {
                    if (e.data.code === 200) {
                        console.log("文件上传成功");
                        this.message("上传文件成功", "可以播放返回音频")
                    } else if (e.data.code === 202) {
                        console.log("分段数据上传成功");
                    } else if (e.data.code === 500) {
                        console.log("文件破损,nothing");
                        // this.uploadFile() 如果这么调用的话会可能找不到递归出口
                    } else if (e.data.code === 502) {
                        console.log("分段破损,分段重传");
                        this.sendBlob(blob, allcode, index, allindex)
                    }

                })
            }
            reader.readAsBinaryString(blob)
        },
uploadFile() {
            var file = this.$refs.file.files[0]
            var segments = this.sliceFile(file)
            console.log(file);
            console.log(segments)
            var fileReader = new FileReader()
            fileReader.onload = (e) => {
                var allcode = SparkMD5.hashBinary(e.target.result)
                for (let index = 0; index < segments.length; index++) {
                  	//该函数 内部post请求是异步的
                    this.sendBlob(segments[index], allcode, index, segments.length)
                }
            }
            fileReader.readAsBinaryString(file)
        },

html核心代码如下:

<input ref="file" type="file" />
<button @click="uploadFile" value="点击上传"></button>

4.后端案例代码

一开始我没意识到这个小小的案例尽然设计到了多线程的问题,于是将HashMap改成HashTable来存储分段数据

@PostMapping("/audio/filesegment/upload")
public Result reciveFileSeg(MultipartFile file,
                                String segcode,
                                String allcode,
                                Integer index,
                                Integer allindex) throws IOException {
        System.out.println(Arrays.toString(services));
        if(!container.containsKey(allcode)){
            container.put(allcode,new Hashtable<>()); //hashmap具有多线程问题
        }
        int len = file.getBytes().length;
        byte [] source = new byte[len];
        ArrayUtil.copy(file.getBytes(),source,len);
//        source[0] += RandomUtil.randomInt(0,2); 检验重传
        if (!segcode.equals(DigestUtil.md5Hex(source))){
            return new Result(502,"分段数据损坏",null);
        }
        //分段数据完整
        Map<String,byte []> map = container.get(allcode);
        map.put(String.valueOf(index),source); //不需要再写入container中了
        if (map.keySet().size() == allindex){
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            //排序为了分段顺序正确
            Object [] keys = map.keySet().stream().sorted(new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return Integer.valueOf(o1)-Integer.valueOf(o2);
                }
            }).toArray();
            for (int i = 0; i < keys.length; i++) {
                byte [] seg = map.get(keys[i]);
                buffer.write(seg);
            }
            byte [] fileByte = buffer.toByteArray();
            String fileMd5 = DigestUtil.md5Hex(fileByte);
            if (allcode.equals(fileMd5)){
                container.remove(allcode); //删除放在map中的数据
                System.out.println("完全上传成功");
                FileUtil.writeBytes(fileByte,FileUtil.newFile(PATH+allcode));

                //todo 作某些服务 可以传递serviceId代表具体的音频增强的功能,同时也可以处理心音和声学事件

                return new Result(200,"文件完整上传",null);

            }else{
                System.out.println("完整文件校验失败");
                return new Result(500,"完整文件校验失败",null);
            }
        }

//        set(index, Arrays.asList(source)); //将数组转换为List
        return new Result(202,"分段数据上传成功",null);
    }

总结

如果有更好的思路或者疑问,欢迎留言。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340