目录
作者:watermelo617
涉及领域:Vue、SpingBoot、Docker、LLM、python
--------------------------温柔地对待温柔的人,包容的三观就是最大的温柔。--------------------------
分片上传技术全解析:原理、优势与应用(含简单实现源码)
一、什么是分片上传
分片上传(Chunked Upload)是将大文件分成多个较小的部分(分片)来逐个上传到服务器。上传完成后,服务器将这些分片重新组装成原始文件。这个过程通常包括以下几个步骤:
- 分片:文件被切割成多个小的片段,每个片段的大小通常是预定义的。
- 上传:每个分片被单独上传到服务器。上传过程中,通常会附带分片的索引和其他元数据。
- 组装:服务器接收到所有分片后,将它们按正确的顺序重新组装成完整的文件。
- 确认:完成组装后,服务器可以返回一个确认响应,表示文件上传成功。
二、分片上传解决了什么问题
分片上传是一个有效的处理大文件上传问题的方案,它通过将文件分割为小片段来提高上传的可靠性和效率,并确保数据的完整性。在网络环境环境不好,存在需要重传风险的场景,分片上传能有效提升上传的体验。
其具体优势在于:
- 大文件上传的稳定性:对于大文件,直接上传可能会因为网络问题、超时等导致上传失败。分片上传可以在单个分片上传失败时仅重新上传失败的分片,而不是整个文件,提高了上传的成功率。
- 减少内存使用:分片上传可以将大文件拆分为较小的片段,这样可以减少内存消耗并优化上传性能。
- 断点续传:分片上传支持断点续传。即使上传过程中发生了中断,用户可以从中断的位置继续上传,不必从头开始。
- 负载均衡:可以在多个服务器之间分配分片上传任务,提高系统的负载均衡能力。
三、分片上传的本质
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开发:
你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解
最细最有条理解析:事件循环(消息循环)是什么?进程与线程的定义、关系与差异
通过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的表格排序功能
优质前端组件库: