SpringBoot+vue 大文件分片下载

news2024/9/24 7:16:59

学习链接

SpringBoot+vue文件上传&下载&预览&大文件分片上传&文件上传进度

Vue+SpringBoot实现文件的分片下载

video标签学习 & xgplayer视频播放器分段播放mp4(Range请求交互过程可以参考这个里面的截图)

代码

FileController

这里面的代码实现,完全可以参考ResourceHttpRequestHandler#handleRequest

@RestController
public class FileController {

    private static final int BUFFER_SIZE = 4 * 1024;

    @RequestMapping(path = "chunkdownload", method = {RequestMethod.HEAD, RequestMethod.POST})
    public void chunkdownload(HttpServletRequest request, HttpServletResponse response) throws Exception {

        File file = new File("D:/usr/test/demo.mp4");

        // 文件总大小
        long fileSize = file.length();

        // 设置 Content-Type 和 相关响应头
        // (这里分片下载响应头设置, 其实可以参考ResourceHttpRequestHandler#handleRequest,
        //  和 video标签学习 & xgplayer视频播放器分段播放mp4 - https://blog.csdn.net/qq_16992475/article/details/130945997)
        response.setContentType("application/octect-stream;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
        response.setHeader("Accept-Ranges", "bytes");

        // 检查请求头中是否有Range请求头,
        // (可参考:video标签学习 & xgplayer视频播放器分段播放mp4 - https://blog.csdn.net/qq_16992475/article/details/130945997)
        String rangeHeader = request.getHeader("Range");

        // 没有Range请求头, 则下载整个文件
        if (rangeHeader == null) {

            response.setHeader("Content-Length", String.valueOf(fileSize));
            InputStream in = new FileInputStream(file);
            OutputStream out = response.getOutputStream();
            // 字节缓冲数组
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead = -1;
            // 读取多少, 写多少, 直到读取完毕为止
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
            in.close();
            out.close();

        } else {

            // 分片下载
            // (可参考: 参考ResourceHttpRequestHandler#handleRequest中的做法)

            // 开始索引
            long start = 0;

            // 结束索引
            long end = fileSize - 1;

            // 获取Range请求头的范围, 格式为:Range: bytes=0-8055,
            // (其中可能没有结束位置, 若没有位置, 取文件大小-1)
            String[] range = rangeHeader.split("=")[1].split("-");

            // 如果Range请求头中没有结束位置, 取文件大小-1
            if (range.length == 1) {

                start = Long.parseLong(range[0]);

                end = fileSize - 1;

            } else {

                // 解析开始位置 和 结束位置
                start = Long.parseLong(range[0]);

                end = Long.parseLong(range[1]);
            }

            // 此次要写出的数据
            long contentLength = end - start + 1;

            // 返回头里存放每次读取的开始和结束字节
            response.setHeader("Content-Length", String.valueOf(contentLength));
            // 响应状态码206
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

            // Content-Range响应头格式为:Content-Range: bytes 0-8055/9000
            response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize);

            InputStream in = new FileInputStream(file);
            OutputStream out = response.getOutputStream();

            // 跳到第start字节
            in.skip(start);

            // 字节缓冲数组
            byte[] buffer = new byte[BUFFER_SIZE];

            // 读取的字节数量
            int bytesRead = -1;

            // 写出的字节数量
            long bytesWritten = 0;


            while ((bytesRead = in.read(buffer)) != -1) {

                // 如果 已写入的数据 + 当前已读到的数据 超过了 此次要写出的数据, 则只能写入请求范围内的数据
                if (bytesWritten + bytesRead > contentLength) {
                    out.write(buffer, 0, (int) (contentLength - bytesWritten));
                    break;
                } else {
                    out.write(buffer, 0, bytesRead);
                    bytesWritten += bytesRead;
                }
            }
            in.close();
            out.close();

        }

    }

}

ChunkDownload.vue

  • 先发1个head请求,获取到文件的大小
  • 再发post请求,获取每个分片(其中为了简单理解,就不引入async-await的使用了)
  • 将获取的每个分片组合为单个文件
<template>
    <div class="gap">
        <el-button @click="downloadChunks">分片下载demo.mp4</el-button>
    </div>
</template>

<script>
import axios from 'axios'

export default {
    name: 'ChunkDownload',
    components: {
    },
    methods: {
        downloadChunks() {
            const chunkdownloadUrl = 'http://localhost:8085/chunkdownload'

            // 分片下载大小 5MB
            const chunkSize = 1024 * 1024 * 5;

            // 文件总大小(需要请求后端获得)
            let fileSize = 0;

            axios
                .head(chunkdownloadUrl)
                .then(res => {

                    // 定义 存储所有的分片的数组
                    let chunks = [];

                    // 获取文件总大小
                    fileSize = res.headers['content-length']

                    // 计算分片数量
                    const chunksNum = Math.ceil(fileSize / chunkSize)

					// 定义下载文件分片的方法
                    function downloadChunkFile(chunkIdx) {

                        if (chunkIdx >= chunksNum) {
                            alert('分片索引不可超过分片数量')
                            return
                        }

                        let start = chunkIdx * chunkSize
                        let end = Math.min(start + chunkSize - 1, fileSize - 1)
                        const range = `bytes=${start}-${end}`;

                        axios({
                            url: chunkdownloadUrl,
                            method: 'post',
                            headers: {
                                Range: range
                            },
                            responseType: 'arraybuffer'
                        }).then(response => {
                            chunks.push(response.data)
                            if(chunkIdx == chunksNum - 1) {
                                // 下载好了
                                console.log(chunks, 'chunks');
                                // 组合chunks到单个文件
                                const blob = new Blob(chunks);
                                console.log(blob, 'blob');
                                const link = document.createElement('a');
                                link.href = window.URL.createObjectURL(blob);
                                link.download = 'demo.mp4';
                                link.click();
                                return
                            } else {
                                ++chunkIdx
                                downloadChunkFile(chunkIdx)
                            }
                        })
                    }

                    downloadChunkFile(0)

                })


        }
    }
}
</script>

<style>
.gap {
    padding: 10px;
}
</style>

测试

在这里插入图片描述

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

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

相关文章

HTML|计算机网络相关

1.三次握手 第一次握手&#xff1a;客户端首先向服务端发送请求。 第二次握手&#xff1a;服务端在接收到客户端发送的请求之后&#xff0c;需要告诉客户端已收到请求。 第三次握手&#xff1a;客户端在接收到服务端发送的请求和确认信息之后&#xff0c;同样需要告诉服务端已…

python并发编程(多线程、多进程、多协程)

文章截图来源来源B站&#xff1a;蚂蚁学python 引入并发&#xff0c;就是为了提升程序运行速度 1、基础介绍 1-1 CPU密集型计算、IO密集型计算 1-2 多进程、多线程、多协程对比 2、全局解释器锁GIL 2-1 python速度慢的两大原因 2-2 GIL是什么 2-3 为什么有GIL这个东西 2-4 怎样…

Vue [Day3]

Vue生命周期 生命周期四个阶段 生命周期函数&#xff08;钩子函数&#xff09; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale…

企业服务器数据库中了devos勒索病毒怎么办如何解决预防勒索病毒攻击

随着科学技术的不断发展&#xff0c;计算机可以帮助我们完成很多重要的工作&#xff0c;但是随之而来的网络威胁也不断提升。近期&#xff0c;我们收到很多企业的求助&#xff0c;企业的服务器数据库遭到了devos勒索病毒攻击&#xff0c;导致系统内部的许多重要数据被加密无法正…

1310. 数三角形

题目链接&#xff1a;https://www.acwing.com/problem/content/1312/ 首先不考虑三点共线的情况一共有 种&#xff0c;现在来计算三点共线的情况 1.三点在一条直线上 2.三点在一条竖线上 3.三点在一条斜线上&#xff0c;正反斜线对称&#xff0c;仅需考虑一边的情况 如果…

考研数学Note1—划分框架

calculus 微积分教会我为什么椭圆的面积 π \pi πab. 隐函数求导Rule 如何理解Lagrange求函数极值&#xff1f; 万物可积&#xff08;所有的函数都能找到原函数?&#xff09;——数即宇宙 线性代数 It’s doubtless that Gitmind&Blog is best place for taking note…

Django Rest_Framework(二)

文章目录 1. http请求响应1.1. 请求与响应1.1.1 Request1.1.1.1 常用属性1&#xff09;.data2&#xff09;.query_params3&#xff09;request._request 基本使用 1.1.2 Response1.1.2.1 构造方式1.1.2.2 response对象的属性1&#xff09;.data2&#xff09;.status_code3&…

“三个高度”写作提纲30例

1.充分把握“三个高度” 全面推进全过程人民民主的基层实践 从坚定政治信仰的高度坚持正确方向 从坚定制度自信的高度把握完整链条 从确保落地见效的高度强化组织保障 2. “三个高度”扎实推进安全生产工作 一是着眼大局&#xff0c;高度负责。 二是立足长远&#xff0c;高…

macOS下Django环境搭建

1. macOS升级pip /Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip 2. 卸载Python3.9.5版本 $ sudo rm -rf /usr/local/bin/python3 $ sudo rm -rf /usr/local/bin/pip3 $ sudo rm -rf /Library/Frameworks/Python.framework 3. 安装P…

servlet接受参数和乱码问题

servlet接受参数和乱码问题 1、乱码问题 1&#xff09;get请求 传输参数出现中文乱码问题&#xff1a; 如果还存在问题&#xff1a; 2&#xff09;post请求 传输参数出现中文乱码问题&#xff1a; 2、接受参数&#xff1a; 3、登录注册案例

【瑞吉外卖项目复写】基本部分复写笔记

Day1 瑞吉外卖项目概述 mysql的数据源配置 spring:datasource:druid:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/regie?serverTimezoneAsia/Shanghai&useUnicodetrue&characterEncodingutf-8&zeroDateTimeBehaviorconvertTo…

智慧工地云平台源码,基于微服务+Java+Spring Cloud +UniApp +MySql开发

智慧工地可视化系统利用物联网、人工智能、云计算、大数据、移动互联网等新一代信息技术&#xff0c;通过工地中台、三维建模服务、视频AI分析服务等技术支撑&#xff0c;实现智慧工地高精度动态仿真&#xff0c;趋势分析、预测、模拟&#xff0c;建设智能化、标准化的智慧工地…

MySQL数据库面试题:如何定位慢查询?

MySQL数据库面试题&#xff1a;如何定位慢查询&#xff1f; 面试官&#xff1a;MySQL中&#xff0c;如何定位慢查询&#xff1f; 候选人&#xff1a;嗯~&#xff0c;我们当时做压测的时候有的接口非常的慢&#xff0c;接口的响应时间超过了2秒以上&#xff0c;因为我们当时的系…

【关于反馈电路的放电问题】2022-1-16

缘由关于反馈电路的放电问题 - 电源技术论坛 - 电子技术论坛 - 广受欢迎的专业电子论坛!图中的副绕组反馈给三极管基极&#xff0c;一般都是说通过三极管充电正反馈三极管导通&#xff0c;放电时负反馈三极管截止&#xff0c;负反馈时&#xff0c;电容C3是通过哪个回路放电的呢…

基于Open3D的点云处理15-特征点

Intrinsic shape signatures (ISS) 参考 ISS关键点: 基本原理是避免在沿主要方向表现出类似分布的点上检测关键点&#xff0c;在这些点上无法建立可重复的规范参考框架&#xff0c;因此后续描述阶段很难变得有效。在剩余点中&#xff0c;显着性由最小特征值的大小决定,以便仅包…

2685. 统计完全连通分量的数量;2718. 查询后矩阵的和;1600. 王位继承顺序

2685. 统计完全连通分量的数量 核心思想&#xff1a;枚举所有的连通分量&#xff0c;然后判断这些连通分量是不是完全连通分量&#xff0c;完全连通分量满足边数2e 点数v(v-1)。 2718. 查询后矩阵的和 核心思想&#xff1a;后面的改变更重要&#xff0c;所以我们直接逆向思维…

无脑入门pytorch系列(二)—— torch.mean

本系列教程适用于没有任何pytorch的同学&#xff08;简单的python语法还是要的&#xff09;&#xff0c;从代码的表层出发挖掘代码的深层含义&#xff0c;理解具体的意思和内涵。pytorch的很多函数看着非常简单&#xff0c;但是其中包含了很多内容&#xff0c;不了解其中的意思…

Spring源码——初识Spring容器

Spring源码之工厂&#xff08;容器&#xff09; 为什么把Spring的工厂又叫做容器呢&#xff1f; 工厂的责任是创建对象&#xff0c;但是创建完对象后还要进行存储&#xff08;针对于单例的对象来讲&#xff09;&#xff0c;以供其他地方使用&#xff0c;这就是容器。为了能存…

STL学习

STL 泛化编程template函数模板类模板 iterator迭代器C array(STL array)容器 STL中文名为标准库,是C标准的规定并且提供了自己编写STL的接口&#xff0c;在编译器实现中统一的分成立几个容器头文件和几个其他的头文件来完成数据结构和算法的抽象&#xff0c;现在编译器使用的是…

FDM3D打印系列——超可动可变形机体打印

大家好&#xff0c;我是阿赵。继续来分享一下3D打印的成果。   这次打印的对象不得了&#xff0c;是超时空要塞系列的可变形VF战机。打印完这个模型&#xff0c;绝对是学习到了很多的东西&#xff0c;下面给大家分享一下。 一、成果展示&#xff1a; 不要怀疑&#xff0c;不…