文件上传下载系列——大文件分片上传

news2024/12/23 17:27:09

文章目录

    • 概述
    • 实现步骤:
      • 应用场景
    • 代码实操:
      • 前端:
        • 文件切片:
        • 分片上传:
      • 后端:
        • 校验和保存:
        • 合并文件片段:
        • 完成上传:
    • 总结
      • 优点:
      • 缺点:
    • 升华

概述

  文件分片上传又叫文件切片上传,是将大文件切分成小的文件片段,分别上传到服务器,并在服务器端将这些文件片段合并成完整的文件。

在这里插入图片描述



实现步骤:

  假设有一个需要上传的文件 “example.jpg”,大小为 102 MB。我们将其切分为大小为 5 MB 的文件片段进行上传,要进行下面几个步骤:

  1. 客户端将 “example.jpg” 切分成 21 个文件片段,每个文件片段大小为 5 MB,最后一个分片大小不足5MB。

  2. 客户端逐个将文件片段上传到服务器,按顺序依次上传。每个文件片段都携带对应的分片索引,例如:第一个文件片段携带索引 1,第二个文件片段携带索引 2,依此类推。

  3. 服务器端接收到文件片段后,对每个文件片段进行校验和保存。校验和算法如 MD5 或 SHA1 可以用于校验文件片段的完整性。服务器将每个文件片段保存到临时存储区。

  4. 当所有文件片段上传完成后,服务器根据文件片段的顺序或索引,将这些文件片段按照正确的顺序合并,生成完整的文件 “example.jpg”。

  5. 服务器将完整的文件保存到指定的目标位置,并通知客户端上传成功。

应用场景

  • 大文件上传:在上传大文件时,文件分片可以减小每个请求的数据量,降低传输失败率,同时可以在上传失败后,只需重新上传丢失的分片,而不需要重新上传整个文件。

  • 视频直播:直播时,需要将视频实时传输到服务器,但由于网络传输不稳定,可能导致视频传输中断。通过将视频分片上传,可以减小每个请求的数据量,降低视频传输失败率。

  • CDN 分发:CDN 分发中使用文件分片上传可以提高文件上传速度和分发速度。CDN 节点会缓存文件的分片,然后在 CDN 节点之间进行分发,以加速文件的传输。



代码实操:

前端:

文件切片:

  将要上传的大文件按照固定大小或指定的分片大小进行切片。通常使用二进制流方式切割,确保每个文件片段的大小相同或接近。

分片上传:

  将每个文件片段逐个上传到服务器。上传可以通过HTTP协议的POST请求或者其他上传协议进行,每个文件片段都携带对应的分片索引或标识信息。

  代码如下:

// 设置每个分片的大小(字节)
const CHUNK_SIZE = 1 * 1024 * 1024; // 1MB

// 获取文件的MD5码
function getMD5(file) {
  // 在这里实现获取文件的MD5码的逻辑
  // 返回文件的MD5码
  // 可以使用第三方库或自己实现MD5算法来计算文件的MD5码
}

// 分片上传文件
function uploadFile(file) {
  const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
  let currentChunk = 0;

  // 递归上传分片
  function uploadChunk() {
    const start = currentChunk * CHUNK_SIZE;
    const end = Math.min(start + CHUNK_SIZE, file.size);
    const chunk = file.slice(start, end);
    const formData = new FormData();

    formData.append('file', chunk);//文件块
    formData.append('chunkIndex', currentChunk); //当前块号
    formData.append('totalChunks', totalChunks); //总块数
    formData.append('chunkMD5', getMD5(chunk)); //当前块的MD5
    formData.append('fileName', file.name); //文件名

    // 使用axios发送分片数据到服务器
    axios.post('/upload', formData)
      .then(response => {
        // 分片上传成功
        currentChunk++;

        if (currentChunk < totalChunks) {
          // 继续上传下一个分片
          uploadChunk();
        } else {
          // 所有分片上传完成
          console.log('文件上传完成');
        }
      })
      .catch(error => {
        // 分片上传失败
        console.error('分片上传失败:', error);
      });
  }

  // 开始上传第一个分片
  uploadChunk();
}

// 选择文件并触发上传
const fileInput = document.getElementById('file-input');
fileInput.addEventListener('change', (event) => {
  const file = event.target.files[0];

  if (file) {
    uploadFile(file);
  }
});


后端:

校验和保存:

  在服务器端接收到文件片段后,对每个文件片段进行校验,使用校验和算法如MD5或SHA1计算校验和,确保文件片段的完整性。同时,将每个文件片段保存到临时存储区,通常是磁盘或内存。

合并文件片段:

  在所有文件片段上传完毕后,服务器端根据文件片段的顺序或标识,将这些文件片段按照正确的顺序进行合并,生成完整的文件。

完成上传:

  当文件合并完成后,将生成的完整文件保存到指定的目标位置,并返回上传成功的标识或信息给客户端,表示文件上传完成。

  代码如下:

@Controller
public class UploadController {

    private final static String utf8 ="utf-8";
    @RequestMapping("/upload")
    @ResponseBody
    public void upload(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //分片
        response.setCharacterEncoding(utf8);//向浏览器输出 统一用该编码输出
        Integer schunk = null;//当前是哪个分片
        Integer schunks = null;//总分片数
        String name = null;//文件名
        String uploadPath = "E:\\ceshi";//文件临时存储目录,存储文件片
        BufferedOutputStream os = null;//文件合并时用的流
        try{
            DiskFileItemFactory factory = new DiskFileItemFactory();//设计文件上传参数
            factory.setSizeThreshold(1024);//缓冲区大小1024字节(文件先读到内存中,在往硬盘里写,中间需要缓存区)
            factory.setRepository(new File(uploadPath));//设置临时目录
            ServletFileUpload upload = new ServletFileUpload(factory);//upload帮助解析request
            upload.setFileSizeMax(5l *1024l *1024l*1024l);//设置参数,多文件上传,限制限制单个文件大小最大为5G
            upload.setSizeMax(10l *1024l *1024l*1024l);//限制所有文件最大为10G
            List<FileItem> items = upload.parseRequest(request);
            //解析文件
            for(FileItem item : items){
                //判断是否是文件对象
                if(item.isFormField()){
                    //取出分片信息
                    if("chunkIndex".equals(item.getFieldName())){
                        schunk = Integer.parseInt(item.getString(utf8));//当前是哪个分片
                    }
                    if("totalChunks".equals(item.getFieldName())){
                        schunks = Integer.parseInt(item.getString(utf8));//总分片数
                    }
                    if("chunkMD5".equals(item.getFieldName())){
                        fileMD5 = item.getString(utf8);//文件块MD5
                    }
                    if("fileName".equals(item.getFieldName())){
                        name = item.getString(utf8);//文件名
                    }
                }
            }
            for(FileItem item : items){
                if(!item.isFormField()){
                    String temFileName = name;//临时目录
                    if(name != null){
                        if(schunk != null){
                            temFileName = schunk +"_"+name;//生成临时文件
                        }
                        File temFile = new File(uploadPath,temFileName);
                        //给临时文件生成MD5,和前端传来的MD5比较的代码略
                        //如果文件不存在就上传
                        if(!temFile.exists()){
                            item.write(temFile);
                        }
                    }
                }
            }
            //文件合并,判断是不是传到最后一个分片了
            if(schunk != null && schunk.intValue() == schunks.intValue()-1){
                File tempFile = new File(uploadPath,name);//准备临时文件把分片写入文件
                os = new BufferedOutputStream(new FileOutputStream(tempFile));//向流中写入文件信息
                //找到所有分片
                //判断所有分片是都存在
                for(int i=0 ;i<schunks;i++){
                    File file = new File(uploadPath,i+"_"+name);
                    //如果有分片不存在,先休眠,直到所有分片都存在
                    while(!file.exists()){
                        Thread.sleep(100);
                    }
                    //读取分片
                    byte[] bytes = FileUtils.readFileToByteArray(file);
                    os.write(bytes);//写入到流中
                    os.flush();//
                    file.delete();//删除临时文件
                }
                os.flush();
            }
            response.getWriter().write("上传成功"+name);
        }finally {
            try{
                if(os != null){
                    os.close();//关文件流
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

  产生的文件片:
在这里插入图片描述
  合并后删除了这些临时文件
在这里插入图片描述



总结

优点:

  1. 支持大文件上传:文件分片上传能够处理大文件,通过将文件切分成小片段进行上传,可以绕过上传文件大小限制。

  2. 断点续传:由于文件分片上传将文件拆分成多个片段,当上传过程中出现异常或中断时,只需要重新上传失败的片段,而不需要重新上传整个文件,实现了断点续传的功能。

  3. 提高上传效率:文件分片上传能够并行上传多个片段,从而提高上传速度。同时,由于每个片段相对较小,上传的网络延迟对整体上传时间的影响较小。

  4. 服务器资源优化:文件分片上传可以分散服务器的负载,由于每个片段可以在不同的服务器上处理,可以更好地利用服务器资源。

缺点:

  1. 处理逻辑复杂:文件分片上传需要在客户端和服务器端实现分片切割、上传顺序管理、校验和合并等复杂的逻辑,增加了开发和维护的难度。

  2. 需要额外存储空间:文件分片上传需要在服务器端临时存储每个文件片段,可能会占用额外的存储空间。

  3. 管理和维护成本:文件分片上传需要处理分片上传的逻辑,并管理临时文件的存储和清理,增加了系统管理和维护的复杂度。



升华

  除了文件分片传输,还有其他类似的数据分片传输情况。

  • TCP报文分段传输:在TCP协议中,会将大的数据包分割成多个较小的报文段进行传输。这是由于TCP协议对数据包大小进行了限制,根据网络传输的情况和MTU(最大传输单元)的限制,TCP会将数据切割成适当的大小进行分段传输,以确保数据的可靠传输。

  • IP分片传输:在IP协议中,当数据包的大小超过网络的MTU(最大传输单元)时,IP层会将数据包分割成多个较小的分片进行传输。接收方会重新组装这些分片以还原原始的数据包。

  • 数据库分页查询:在数据库查询中,当结果集非常大时,可以使用分页查询来分割结果集并逐页进行传输。客户端可以根据需要逐页获取数据,而不必一次性获取所有数据。

  • 视频流传输:在实时视频流传输中,视频数据通常会被分割成小的数据包,然后通过网络进行传输。这样可以降低延迟,并支持实时的流媒体播放。

  • 大数据处理:在大数据处理中,数据可能会被分割成小的块或分区进行并行处理。这样可以利用分布式计算框架的能力,并提高处理效率。

  这些都是将大的数据切割成小的片段进行传输或处理的情况。通过分片传输,可以提高数据传输的效率、降低网络延迟,并支持更灵活的数据处理和传输方式。
在这里插入图片描述

觉得有用就点个赞吧!

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

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

相关文章

spring cache和SpEL表达式

spring cache的 CachePut如果方法体内容变了,缓存还有效吗 当使用CachePut注解时&#xff0c;其目的是更新缓存&#xff0c;即在方法执行完毕后将返回结果加入缓存中。如果方法体内容发生变化&#xff0c;则更新后的结果会被加入缓存中&#xff0c;而缓存中原来的数据会被替换…

ORACLE 新闻速递 ORACLE 23C 免费提供给开发者 为什么???

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

利用AI将脑信号解码为神经义肢控制

机器学习和人工智能的快速发展&#xff0c;它们已经产生了一系列能够学习大量数据之间复杂非线性关系的神经网络模型。深度学习算法在各种应用中取得了巨大的成功&#xff0c;包括计算机视觉、自然语言处理和机器人类等。最近&#xff0c;神经网络的中一项引人注目的应用是通过…

B 站构建实时数据湖的探索和实践

摘要&#xff1a;本文整理自 bilibili 大数据实时团队资深开发工程师周晖栋&#xff0c;在 Flink Forward Asia 2022 实时湖仓专场的分享。本篇内容主要分为四个部分&#xff1a; 1. 背景和痛点 2. 场景探索 3. 基建优化 4. 总结和展望 Tips&#xff1a;点击「阅读原文」查看原…

VMware之esxcli命令

一、esxcli命令简介 esxcli命令是用于管理VMware ESXi主机的命令行实用程序。它允许管理员在主机上执行各种管理任务&#xff0c;如配置网络设置、存储、虚拟机等。实际上我们在控制台的所有操作最终都会转换为命令在后台执行。以下是一些常见的esxcli命令&#xff1a; esxcli…

第三章 存储系统

1. 主存简单模型和寻址概念 存储器芯片的基本结构 寻址 2.存储器RAM 两种RAM的比较 DRAM的刷新 3.ROM(只读存储器) 为什么需要ROM&#xff1f; 因为RAM里面的数据容易丢失。 4.存储器基本概念 存储器的分类 性能指标 数据的宽度&#xff1a;在一个存储周期中存储的位数 层…

感烟探测器漂移补偿

3.8 慢速发展火灾响应性能 3.8.1 探测器的漂移补偿功能不应使探测器对慢速发展火灾的响应性能产生明显影响。 3.8.2 当无法用模拟烟气浓度缓慢增加的方法评估探测器对慢速发展火灾响应性能时&#xff0c;可以通过物理试验和模拟试验对电路和/或软件分析确定。 3.8.3 探测器评估…

丰富多彩 | Android 14 Beta 2 发布

作者 / Dave Burke, VP of Engineering Google I/O 的大幕已经拉开&#xff0c;我们也为大家带来了 Android 14 的第二个 Beta 版。我们在 Google I/O 上为大家准备了许多精彩的演讲&#xff0c;包括对 Android 14 许多新功能的详细介绍。这次发布的 Beta 2 聚焦相机和媒体、隐…

只要学会这些AI工具,一个人就是一家营销咨询公司

本教程收集于:AIGC从入门到精通教程 只要学会这些AI工具,一个人就是一家营销咨询公司 随着AI工具的不断涌现,您只需掌握市面上热门的AI工具,便可独自开展营销咨询公司。通过一系列AI工具,您可以为企业提供全案服务,收获丰厚回报。 例如,在协助一家美妆初创公司出海时,…

上下文工程:基于 Github Copilot 的实时能力分析与思考

上个月在计划为 AutoDev 添加多语言支持时候&#xff0c;发现 GitHub Copilot 的插件功能是语言无关的&#xff08;通过 plugin.xml 分析&#xff09;&#xff0c;便想研究一下它是如何使用 TreeSitter 的。可惜的是&#xff0c;直到最近才有空&#xff0c;研究一下它是如何实现…

印刷和喷绘过程中高精度油墨流量和压力的串级控制解决方案

摘要&#xff1a;针对现有技术在印刷或喷绘设备中油墨流量控制不准确&#xff0c;使得油墨粘稠度产生异常造成批量性质量方面的问题&#xff0c;本文提出了相应的串级控制解决方案&#xff0c;即通过双回路形式同时控制油墨的流量和压力。本解决方案不仅可以保证油墨最终流量的…

【C++ 入坑指南】(07)判断

文章目录 一、if 语句1. 单行格式 if 语句2. 多行格式 if 语句 二、三目运算符三、switch 语句四、算法题4.1 分段函数 判断结构要求程序员指定一个或多个要评估或测试的条件&#xff0c;以及条件为真时要执行的语句&#xff08;必需的&#xff09;和条件为假时要执行的语句&am…

C++ - 使用RBTree来封装Set与Map

前面的文章中我们简单的实现了一个红黑树&#xff0c;实现了它的插入的功能&#xff0c;在本文中我们来对其进行修改并构建Set与Map。 下面我们来从源码中截取一点有关Map与Set的代码&#xff1b; 可以看出&#xff0c;在STL30中构建Map与Set使用的是同一个红黑树模板&#xf…

开箱即用!AI模型库全新发布,一站式在线开发部署!

人工智能学习与实训社区飞桨AI Studio模型库全新升级&#xff01;支持模型创建、上传、托管并一键调用模型线上开发部署&#xff01; 飞桨AI Studio已接入飞桨生态特色模型库&#xff0c;全面覆盖模型领域&#xff0c;包含文心系列大模型、CV、NLP、语音、科学计算、量子计算等…

Linux常用命令——hwclock命令

在线Linux命令查询工具 hwclock 显示与设定硬件时钟 补充说明 hwclock命令是一个硬件时钟访问工具&#xff0c;它可以显示当前时间、设置硬件时钟的时间和设置硬件时钟为系统时间&#xff0c;也可设置系统时间为硬件时钟的时间。 在Linux中有硬件时钟与系统时钟等两种时钟。…

XR文字输入技术

VR/AR/MR&#xff08;统称为XR&#xff09;有望成为个人电脑、手机之后的下一代计算平台&#xff0c;元宇宙的硬件入口。 近年来XR发展迅猛&#xff0c;不仅可以应用于游戏、影视领域&#xff0c;还在军事、医疗、办公、教育等领域有巨大的发展前景。然而相比于XR强大的显示技…

IPWorks IPC .NET 2022.0.85 Crack

IPWorks IPC .NET一个组件库&#xff0c;用于通过命名管道向应用程序添加进程间通信 &#xff08;IPC&#xff09; 功能。包括客户端、服务器和外部进程执行组件&#xff0c;用于进程之间的简单对等通信。 最新的IPWorks IPC现已上市&#xff01;最新版本的 IPWorks IPC 具有现…

我用ChatGPT搞懂GPT技术原理,只问了30个问题,这是极致的学习体验!

自己前段时间写了一篇文章《问了ChatGPT 上百个问题后&#xff0c;我断定ChatGPT可以重塑学习范式&#xff01;》&#xff0c;讲了使用ChatGPT的感受&#xff0c;最近我开始学习GPT的技术原理&#xff0c;原因有三个&#xff1a; 1、工作中有可能要用到GPT&#xff0c;理解GPT的…

智见|中国能建中电工程罗必雄:数能融合为数字中国夯实底座

出品|网易科技《智见访谈》 作者&#xff5c;赵芙瑶 编辑&#xff5c;丁广胜 数字化浪潮的风&#xff0c;吹到了能源结构转型领域。 中国作为全球最大的能源生产国和消费国&#xff0c;正积极推动能源行业的数字化和智能化建设。数字化智能化升级在能源产业中被视为一项重要的战…

Go设计模式--解释器模式

大家好&#xff0c;这里是每周都在陪你一起进步的网管&#xff5e;&#xff01;今天继续学习设计模式—解释器模式 解释器模式是一种行为设计模式&#xff0c;可以用来在程序里创建针对一个特点领域语言的解释器&#xff0c;用于处理解释领域语言中的语句。换句话说&#xff0c…