分片上传技术全解析:原理、优势与应用(含简单实现源码)

目录

分片上传技术全解析:原理、优势与应用(含简单实现源码)

一、什么是分片上传

二、分片上传解决了什么问题

三、分片上传的本质

3.1、分片上传的本质意义

3.2、分片上传的风险与规避方式

①网络问题

②服务器端错误

③文件完整性校验

3.3、分片上传中的重试机制与断点续传

①重试机制

②断点续传

四、基于js的简单实现案例

五、总结


作者:watermelo617

涉及领域:Vue、SpingBoot、Docker、LLM、python


--------------------------温柔地对待温柔的人,包容的三观就是最大的温柔。--------------------------


分片上传技术全解析:原理、优势与应用(含简单实现源码)

一、什么是分片上传

分片上传(Chunked Upload)是将大文件分成多个较小的部分(分片)来逐个上传到服务器。上传完成后,服务器将这些分片重新组装成原始文件。这个过程通常包括以下几个步骤:

  1. 分片:文件被切割成多个小的片段,每个片段的大小通常是预定义的。
  2. 上传:每个分片被单独上传到服务器。上传过程中,通常会附带分片的索引和其他元数据。
  3. 组装:服务器接收到所有分片后,将它们按正确的顺序重新组装成完整的文件。
  4. 确认:完成组装后,服务器可以返回一个确认响应,表示文件上传成功。

二、分片上传解决了什么问题

分片上传是一个有效的处理大文件上传问题的方案,它通过将文件分割为小片段来提高上传的可靠性和效率,并确保数据的完整性。在网络环境环境不好,存在需要重传风险的场景,分片上传能有效提升上传的体验。

其具体优势在于:

  1. 大文件上传的稳定性:对于大文件,直接上传可能会因为网络问题、超时等导致上传失败。分片上传可以在单个分片上传失败时仅重新上传失败的分片,而不是整个文件,提高了上传的成功率。
  2. 减少内存使用:分片上传可以将大文件拆分为较小的片段,这样可以减少内存消耗并优化上传性能。
  3. 断点续传:分片上传支持断点续传。即使上传过程中发生了中断,用户可以从中断的位置继续上传,不必从头开始。
  4. 负载均衡:可以在多个服务器之间分配分片上传任务,提高系统的负载均衡能力。

三、分片上传的本质

3.1、分片上传的本质意义

分片上传的本质就是将大文件分割成多个较小的部分,逐个上传到服务器,然后在服务器端将这些部分重新组合成完整的文件。这种方法不仅解决了大文件上传的问题,还能优化网络带宽和提高上传效率。

大多数情况下,服务器会负责将接收到的分片重新组装成完整的文件。服务器通常会根据分片的索引顺序将它们拼接起来。在某些情况下,客户端可能会将所有分片上传到服务器后,由客户端自行处理合并操作(通常较少见)。

3.2、分片上传的风险与规避方式

①网络问题

网络中断可能导致某些分片上传失败。为了应对这种情况,可以实现重试机制和断点续传功能。

②服务器端错误

服务器在接收和组装分片时可能会发生错误。应确保服务器有适当的错误处理和日志记录机制。

③文件完整性校验

为了确保文件在上传和组装过程中没有发生损坏,通常会计算文件的哈希值,并在上传完成后进行校验。在第四部分案例代码中使用了SparkMD5来计算文件的MD5哈希值,以确保文件的一致性和完整性。

3.3、分片上传中的重试机制与断点续传

这些技术可以结合使用,以提高上传的鲁棒性。重试机制 确保分片上传的稳定性,断点续传支持上传中断后的恢复。

①重试机制

重试机制是在上传过程中,如果遇到网络问题、服务器错误或其他上传失败的情况,自动重新尝试上传失败的部分(通常是分片上传中会使用重试机制)。适用于网络不稳定或服务器偶尔出现故障的情况。可以做到:

  • 提高成功率:自动处理上传失败的情况,提高上传成功率。
  • 错误恢复:在上传过程中遇到错误时,可以自动恢复,无需用户干预。
  • 可配置性:重试次数和时间间隔可以进行配置,以适应不同的需求和网络环境。
②断点续传

断点续传断点续传是允许在上传过程中中断后,能够从中断的位置继续上传,而不是从头开始。这通常涉及记录已上传的部分,并在恢复时跳过这些已上传部分。

一般来说,断点续传是从断开的分片起,重新上传该分片及之后的分片。但也有更精细的实现方式,从精确的分片的断点位置起,这样在中断频繁的网络环境中表现更好,可以避免重复上传已经部分成功的内容。更加高效和灵活,但实现复杂度较高。

断点续传有助于:

  • 中断恢复:在上传过程中,如果发生中断,可以从中断点继续上传,而不是重新上传整个文件。
  • 记录状态:需要在客户端或服务器端记录上传进度。
  • 用户体验:提升用户体验,避免长时间上传过程中的中断带来的困扰。

四、基于js的简单实现案例

前端代码:

javascript 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <input id="upload" type="file" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.2/spark-md5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.7.2/axios.min.js"></script>
    <script>
        // 如何获取input,监听上传事件
        var inputs = document.getElementById('upload')
        inputs.onchange = async () => {
            // console.log(inputs.files, 'file')
            // 获取到上传的文件对象
            var file = inputs.files[0]
            // 创建分片数据
            var chunks = createChunk(file, 50 * 1024 *1024)
           
            // 文件内容创建hash
            var res = await hash(chunks)
            console.log(res, 'hash')
            // 调用发送分片的接口
            uploadChunk(chunks, res, file.name)
        }

        // 创建分片
        function createChunk(file, chunkSize) {
            const result = []
            for (let i = 0; i < file.size; i += chunkSize) {
                result.push(file.slice(i, i + chunkSize))
            }
            return result
        }

        // 根据文件内容创建hash值
        function hash(chunks) {
            return new Promise((resolve) => {
                var spark = new SparkMD5()
                // 递归
                function _read(i) {
                    if (i >= chunks.length) {
                        resolve(spark.end())
                        return
                    }
                    // 获取当前其中的一个片段
                    var blob = chunks[i]
                    // 创建一个FileReader对象
                    var reader = new FileReader()
                    reader.onload = e => {
                    var bytes = e.target.result
                    spark.append(bytes)
                    _read(i + 1)
                    }

                    reader.readAsArrayBuffer(blob)
                }
                _read(0)
            })
        }

        // 分片上传
        function uploadChunk(chunks, hash, fileName) {
            // 文件怎么样传给后端? ok
            // 所有分片上传成功后,有一个通知告诉后端已经上传完成?
            var taskArr = []
            chunks.forEach((chunk, index) => {
                var formdata = new FormData()
                formdata.append('chunk', chunk)
                formdata.append('chunkName', `${hash}-${index}-${fileName}`)
                formdata.append('fileName', fileName)
                var task = axios.post('http://127.0.0.1:3000/upload', formdata, {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    }
                })
                taskArr.push(task)
            });
            Promise.all(taskArr).then(() => {
                // 所有分片上传成功后的结果
                console.log('通知后端')
            })
            
        }
    </script>
</body>

</html>

极简的后端接口(模拟发送请求,不具备实际后端作用):

javascript 复制代码
const express = require('express')
const cors = require('cors')
const app = express()

app.use(cors())

const hostname = '127.0.0.1'
const port = 3000

app.use(express.json())

// 模拟支付
app.post('/upload', (req, res) => {
  res.status(200).json({})
})

app.listen(port, hostname, () => {
  console.log(`Server is running at http://${hostname}:${port}/`)
})

五、总结

分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。

博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

更多优质内容,请关注:

JS语法与Vue开发:

浏览器渲染揭秘:从加载到显示的全过程

Vue 性能革命:揭秘前端优化的终极技巧

属性描述符初探------Vue实现数据劫持的基础

你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解

最细最有条理解析:事件循环(消息循环)是什么?进程与线程的定义、关系与差异

路由通配符,小小的字符有大大的作用,你真的熟悉吗?

管理数据必备!侦听器watch用法详解

什么是深拷贝?深拷贝和浅拷贝有什么区别

对象数据的读取,看这一篇就够了!

通过array.every()实现数据验证、权限检查和一致性检查,array.some与array.every的区别

通过array.some()实现权限检查、表单验证、库存管理、内容审查和数据处理

通过array.map()实现数据转换、创建派生数组、异步数据流处理、搜索和过滤等需求

通过array.reduce()实现数据汇总、条件筛选和映射、对象属性的扁平化、转换数据格式等

通过array.filter()实现数组的数据筛选、数据清洗和链式调用

多维数组操作,不要再用遍历循环foreach了,来试试数组展平的小妙招!

别再用双层遍历循环来做新旧数组对比,寻找新增元素了!

shpfile转GeoJSON且控制转化精度;如何获取GeoJSON?GeoJson结构详解

Mapbox添加行政区矢量图层、分级设色图层、自定义鼠标悬浮框、添加天地图底图等

Element plus拓展:

通过el-tree自定义渲染网页版工作目录,实现鼠标悬浮显示完整名称等

el-table实现动态数据的实时排序,一篇文章讲清楚elementui的表格排序功能

el-table中如何添加渐变色带、多色色带

优质前端组件库:

高效工作流:用Mermaid绘制你的专属流程图

相关推荐
郭二哈9 分钟前
git的使用
大数据·网络·git·elasticsearch
Rubin9315 分钟前
TS 相关
javascript
该用户已不存在19 分钟前
这几款Rust工具,开发体验直线上升
前端·后端·rust
前端雾辰31 分钟前
Uniapp APP 端实现 TCP Socket 通信(ZPL 打印实战)
前端
阿里云大数据AI技术32 分钟前
鹰角网络基于阿里云EMR Serverless StarRocks的实时分析工程实践
数据库·数据分析
无羡仙37 分钟前
虚拟列表:怎么显示大量数据不卡
前端·react.js
云水边43 分钟前
前端网络性能优化
前端
久笙&1 小时前
对象存储解决方案:MinIO 的架构与代码实战
数据库·python·架构
用户51681661458411 小时前
[微前端 qiankun] 加载报错:Target container with #child-container not existed while devi
前端
小指纹1 小时前
河南萌新联赛2025第(六)场:郑州大学
java·开发语言·数据结构·c++·算法