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

目录

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

一、什么是分片上传

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

三、分片上传的本质

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绘制你的专属流程图

相关推荐
薯条不要番茄酱37 分钟前
数据结构-5.二叉树
java·开发语言·数据结构
王大傻092839 分钟前
数据库的性能优化 -- SQL性能优化
数据库·sql·性能优化
那就可爱多一点点40 分钟前
H5页面多个视频如何只同时播放一个?
前端·音视频
YAy172 小时前
CC3学习记录
java·开发语言·学习·网络安全·安全威胁分析
代码小鑫2 小时前
A035-基于Spring Boot的企业内管信息化系统
java·开发语言·spring boot·后端·spring
cleverpeople2 小时前
11.15作业
c语言·开发语言·算法
_.Switch2 小时前
Django SQL 查询优化方案:性能与可读性分析
开发语言·数据库·python·sql·django·sqlite·自动化
Orange分享2 小时前
Jupyter Book 快捷键总结大全
数据分析
H愚公移山H2 小时前
ElasticSearch-全文检索(一)基本介绍
大数据·elasticsearch·全文检索
Amo Xiang2 小时前
Django 2024全栈开发指南(三):数据库模型与ORM操作(上篇)
数据库·django·django-orm