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

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

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);
    }

总结

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

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值