前端大文件分块上传、断点续传
文章目录
- 前端分块上传流程
- 分块上传代码
- 重点
- 1. `let start = currentChunk * chunkSize;`
- 2. `let end = Math.min(file.size, start + chunkSize);`
- 3. `let chunk = file.slice(start, end);`
- 结合断点续传
- 注意事项
大文件上传是一个复杂的过程,尤其是在前端,我们需要考虑用户体验、网络状况、文件完整性等多个方面。
以下是一个使用HTML5的
File API和
XMLHttpRequest进行大文件分块上传的详解和示例代码。
前端分块上传流程
-
选择文件:使用
<input type="file">元素让用户选择文件。 -
读取文件:使用
FileReader API读取文件内容。 -
分块文件:根据设定的大小将文件切割成多个小块。
-
上传分块:使用
XMLHttpRequest或fetch API将每个分块上传到服务器。 -
合并文件:服务器接收到所有分块后,将其合并成完整的文件。
分块上传代码
以下是一个简单的示例代码,展示了如何使用JavaScript实现大文件的分块上传。
<!DOCTYPE html>
<html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>大文件分块上传</title>
</head>
<body> <input type="file" id="fileInput"> <button id="uploadButton">上传</button> <script> const chunkSize = 1024 * 1024; // 每块大小设置为1MB let fileInput = document.getElementById('fileInput'); let uploadButton = document.getElementById('uploadButton'); uploadButton.addEventListener('click', function () { let file = fileInput.files[0]; let chunks = Math.ceil(file.size / chunkSize); // 计算文件块数(文件总大小 / 每块大小)let currentChunk = 0; // 当前上传的块数 function uploadChunk() { if (currentChunk < chunks) { let start = currentChunk * chunkSize; // 计算当前要上传的分片的起始字节位置(已经上传的字节)let end = Math.min(file.size, start + chunkSize); // 计算当前要上传的分片的结束字节位置(当前准备上传的字节)let chunk = file.slice(start, end); let formData = new FormData(); formData.append('chunk', chunk); formData.append('fileName', file.name); formData.append('totalChunks', chunks); formData.append('currentChunk', currentChunk); let xhr = new XMLHttpRequest(); xhr.open('POST', '/upload', true); // 替换为你的上传接口 xhr.upload.onprogress = function (e) { // 监听上传进度if (e.lengthComputable) { console.log((e.loaded / e.total * 100) + '%'); } }; xhr.onload = function () { if (xhr.status === 200) { currentChunk++; uploadChunk(); // 递归上传下一个块 } else { console.error('上传失败'); } }; xhr.send(formData); } else { console.log('上传完成'); } } uploadChunk(); // 开始上传第一个块 }); </script>
</body>
</html>
重点
1. let start = currentChunk * chunkSize;
-
currentChunk:当前要上传的文件分片的索引
如果这是第一个分片,currentChunk 通常是0;如果是第二个分片,它是1,依此类推。
每当当前分片上传成功,便会累加 -
chunkSize:每个分片的大小(以字节为单位)
这通常是一个固定的值,比如1MB(即1024 * 1024字节)或其他合适的值,取决于你的应用需求和网络条件 -
start:currentChunk和chunkSize相乘,你得到了当前分片开始的字节位置。
例如,假设你有一个10MB的文件,并且你决定每个分片大小为1MB:
对于第一个分片(currentChunk = 0),start 会是0 * 1MB = 0,即从文件的开头开始。
对于第二个分片(currentChunk = 1),start 会是1 * 1MB = 1MB,即从文件的1MB位置开始。
对于第三个分片(currentChunk = 2),start 会是2 * 1MB = 2MB,以此类推。
.
2. let end = Math.min(file.size, start + chunkSize);
end 是一个变量,用于表示文件分片上传时的结束字节位置。
这个计算确保了分片不会超出文件的实际大小,并且在最后一个分片时能够正确地设置结束位置。
-
start + chunkSize:计算当前分片应该结束的位置,也就是当前需要上传的文件分片 -
file.size:获取文件的总大小 -
Math.min(...):取上述两个值中的较小者作为当前分片的结束位置
这样做的原因是,当处理文件的最后一个分片时,start + chunkSize 可能会超出文件的实际大小。
为了避免这种情况,我们使用 Math.min 函数来确保 end 不会大于文件的实际大小(file.size)。
或者可以这么理解: let end = start + chunkSize > file.size ? file.size : start + chunkSize
例如,假设你有一个13MB的文件,并且你决定每个分片大小为5MB。即:
-
file.size =
13*1024*1024 => 文件大小为 13631488 字节 -
chunkSize =
5*1024*1024 => 每个分片大小为 5242880 字节 -
let chunks =
Math.ceil(file.size / chunkSize) => 文件块数为3
这时:
-
第一个分片:
start + chunkSize将分别是 0 + 5MB -
第二个分片:
start + chunkSize将分别是 5MB + 5MB -
第三个分片:如果简单地使用 10MB + 5MB,你会得到一个超出文件大小的结束位置(15MB>13MB)。
通过 Math.min,你可以确保第三个分片的结束位置正确地设置为文件的末尾(即13MB):
Math.min(file.size, start + chunkSize)=>Math.min(13*1024*1024, (10+5)*1024*1024)=>13*1024*1024 字节=> 13MB
.
3. let chunk = file.slice(start, end);
在JavaScript中,file.slice(start, end) 是 Blob 对象的一个方法,用于创建一个新的 Blob 对象,该对象包含源 Blob 对象中指定范围内的数据。这通常用于文件操作,特别是当你需要处理文件的一部分(例如,分片上传)时。
file 是一个 Blob 或 File 对象(File 继承自 Blob),而 start 和 end 是指定切片范围的参数:
- start(包含):开始切片的字节偏移量。
- end(不包含):结束切片的字节偏移量。(如果 end 被省略或大于 Blob 的大小,则切片会一直到 Blob 的末尾。)
例如,如果你有一个 File 对象 file,你可以使用 slice 方法来获取文件的部分字节:
var slice1 = file.slice(0, 100); // 获取文件的前100个字节var slice2 = file.slice(100, 200); // 获取从第101个字节开始到第200个字节结束的部分
注意:
- slice 方法不会改变原始的 Blob 或 File 对象
- 它返回一个新的 Blob 对象,该对象包含指定范围内的数据
结合断点续传
断点续传(Resume Download)是一种在网络传输中常见的功能,它允许在下载大文件时,如果由于某种原因(如网络中断、程序崩溃等)导致下载中断,可以从之前中断的地方继续下载,而不是重新下载整个文件。这可以大大节省时间和带宽资源。
// 假设你已经通过某种方式获取了文件对象以及要续传的起始字节位置 startByte
const file = document.querySelector('input[type="file"]').files[0];
const chunkSize = 1024 * 1024; // 1MB 分片大小
const startByte = // 从服务器获取的已下载字节数,用于断点续传
let currentChunk = Math.ceil(startByte / chunkSize); // 计算当前应该开始下载的分片索引 // 分片函数
function createChunk(file, start, end) { const chunk = file.slice(start, end); return new Blob([chunk], { type: file.type });
} // 创建分片并发送请求的函数
function uploadChunk(chunk, chunkIndex) { const formData = new FormData(); formData.append('file', chunk); formData.append('chunkIndex', chunkIndex); formData.append('fileName', file.name); formData.append('chunkSize', chunkSize); formData.append('totalSize', file.size); axios.post('/upload-chunk', formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: progressEvent => { const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); console.log(`Chunk ${chunkIndex + 1} uploaded: ${percentCompleted}%`); }, onDownloadProgress: progressEvent => { // 可以在这里处理下载进度,如果需要的话 } }) .then(response => { if (response.data.success) { console.log(`Chunk ${chunkIndex + 1} uploaded successfully.`); // 检查是否所有分片都已上传完成 if (chunkIndex < Math.ceil(file.size / chunkSize) - 1) { // 上传下一个分片 const nextChunkStart = chunkIndex * chunkSize + chunkSize; const nextChunkEnd = Math.min(nextChunkStart + chunkSize, file.size); const nextChunk = createChunk(file, nextChunkStart, nextChunkEnd); uploadChunk(nextChunk, chunkIndex + 1); } else { // 所有分片上传完成,可以通知服务器合并文件或进行其他操作 console.log('All chunks uploaded successfully.'); } } else { console.error('Chunk upload failed:', response.data.error); } }) .catch(error => { console.error('Chunk upload error:', error); // 可以在这里处理错误,比如重试上传当前分片或全部重新开始 });
} // 开始上传
const firstChunkStart = currentChunk * chunkSize;
const firstChunkEnd = Math.min(firstChunkStart + chunkSize, file.size);
const firstChunk = createChunk(file, firstChunkStart, firstChunkEnd);
uploadChunk(firstChunk, currentChunk);
在这个示例中:
-
createChunk函数用于创建文件的分片 -
uploadChunk函数用于发送包含分片数据的 POST 请求到服务器 -
startByte是从服务器获取的已下载字节数,用于确定从哪里开始上传分片
每次上传一个分片后,如果成功,则检查是否还有剩余的分片需要上传,如果有,则继续上传下一个分片。
请注意,这个示例仅涵盖了前端的部分逻辑。为了实现完整的断点续传功能,你还需要在后端实现相应的逻辑来处理分片上传、合并分片以及存储和检索已上传的分片信息。此外,还需要考虑如何安全地验证和授权上传请求,以避免安全风险。
注意事项
-
文件完整性校验:上传完成后,服务器需要对合并后的文件进行完整性校验,确保文件没有损坏。
-
断点续传:在实际应用中,还需要考虑断点续传的功能,即当上传中断时,可以从上次中断的地方继续上传。
-
上传进度显示:在上传过程中,可以通过
XMLHttpRequest的upload.onprogress事件来显示上传进度。 -
错误处理:在上传过程中可能会遇到各种错误,如网络错误、服务器错误等,需要妥善处理这些错误,并给用户友好的提示。
-
安全性:在处理文件上传时,需要注意安全性问题,如防止恶意文件上传、防止跨站脚本攻击等。
相关文章:
前端大文件分块上传、断点续传
文章目录 前端分块上传流程分块上传代码重点1. let start currentChunk * chunkSize;2. let end Math.min(file.size, start chunkSize);3. let chunk file.slice(start, end); 结合断点续传注意事项 大文件上传是一个复杂的过程,尤其是在前端,我们需…...
使用新版FLIR (FLIR_ADAS_v2) 数据集创建yolo格式数据集(目标检测)
FLIR在2022.1.19发布了新版的FLIR_ADAS_v2,有着更多的类别和数量更丰富的图像。数据集同步注释热图像和无注释RGB图像供参考。本文章主要介绍如何使用FLIR_ADAS_v2中的rgb图像和thermal图像来制作yolo格式数据集。 1.官方数据集下载:FLIR_ADAS_v2数据集…...
PHP发票查验接口未返回正确信息的原因、发票ocr识别接口
发票查验接口未返回正确信息的原因一般有以下几种,第一种可能是接口没有调通,第二种是本身这张发票就是一张错票、假票,第三种可能是税局系统或者网络问题等等。那么,遇到这种情况应该如何解决呢?翔云发票查验接口&…...
RA4000CE为汽车动力传动系统提供解决方案
目前汽车电气化的水平越来越高,其中比较显著的一个发展方向就是将发动机管理系统和自动变速器控制系统,集成为动力传动系统的综合控制(PCM)。作为汽车动力的核心部件,通过电子系统的运用,将外部多个传感器和执行环节的数据进行统一…...
算法中的二阶差分
众所周知,在往区间的每一个数都加上一个相同的数k,进行n次后会得到一个新的数列,如果每次加都循环区间挨个数加上k,这样时间复杂度无疑是O(n^2),很高。这时可以采用一阶差分就可解决,这里默认会一阶差分&am…...
第十五届蓝桥杯Java A组参赛总结
一、比赛 4月13号那天上午9点到下午1点,线上比赛总共4小时。 因为很久没有参加过竞赛了,所以还是很紧张,睡觉都有点睡不好,生怕出什么差错 我参加的是java的A组,两道填空(每道5分)和六道大题…...
springCloudAlibaba集成seata实战(分布式事物详解)
一、分布式事务 1. 事务介绍 1.1 基础概念 事务:保证我们多个数据库操作的原子性,多个操作要么都成功要么都不成功 事务ACID原则 A(Atomic)原子性:构成事务的所有操作,要么都执行完成,要么全部…...
VRTK/SteamVR手柄震动功能
VRTK/SteamVR手柄震动功能 前言代码块 前言 手柄震动功能配合虚拟仿真模块的模拟电击等功能非常方便 代码块 SteamVR_Controller.DeviceRelation.Rightmost是右侧手柄 SteamVR_Controller.DeviceRelation.Leftmost是左侧手柄 var deviceIndex2 SteamVR_Controller.GetDevic…...
MYSQL索引优化方法
👏作者简介:大家好,我是小周同志,25届双非校招生Java选手,很高兴认识大家 📕学习出处:本文是学自小林coding (xiaolincoding.com) 网站的MYSQL图解篇 🔥如果感觉博主的文章还不错的…...
多模态 ——LLaVA 集成先进图像理解与自然语言交互GPT-4的大模型
概述 提出了一种大型模型 LLaVA,它使用 GPT-4 生成多模态语言图像指令跟随数据,并利用该数据将视觉和语言理解融为一体。初步实验表明,LLaVA 展示了出色的多模态聊天能力,在合成多模态指令上的表现优于 GPT-4。 在科学质量保证中…...
文献学习-33-一个用于生成手术视频摘要的python库
VideoSum: A Python Library for Surgical Video Summarization Authors: Luis C. Garcia-Peraza-Herrera, Sebastien Ourselin, and Tom Vercauteren Source: https://arxiv.org/pdf/2303.10173.pdf 这篇文章主要关注的是如何通过视频摘要来简化和可视化手术视频,…...
Unity Android 2021 Release-Notes
🌈Unity Android 2021 Release-Notes 版本更新内容2021.3.34Android: Google play.core package is replaced with separate plugins including play.asset-delivery 2.1.0 to solve PAD related compatibility problem with Android 14.(UUM-54157)2021.3.34Androi…...
Java8新特性--lambda表达式
lambda表达式本质上是一个匿名函数,在lambda表达式中我们只需要关心参数列表以及方法体。优点是可以减少代码量。 1.语法 基本语法:(参数)->表达式 或 (参数) -> {语句;} 2.函数式接口 要了解lambda表达式,首先要了解什么是函数式接口…...
C/C++中设置随机数
前言 我们通常在写一个数据结构后,需要去测试其正确性和性能比较,那在平常手动输入数据的方式太鸡肋,并且不具有普遍性和随机性。基于这个原因,我们必须要掌握设置随机数,不但可以给我们提供更多的数据,还可…...
ARM 三个小灯闪烁
.text .global _start _start: 使能GPIOE的外设时钟 LDR R0,0x50000A28 指定基地址 LDR R1,[R0] 读取r0中的数据保存到r1中 ORR R1,R1,#(0X3<<4) [4]设置为1,表示 STR R1,[R0] 将修改之后的值放回去 设置PE10,PE8为输出 LDR R0,0X50006000…...
创业之路:从市场洞察到产品实现的全方位指南
创业是一项挑战性的旅程,需要综合考虑市场、产品、技术、团队等多个方面。在这篇文章中,我们将深入探讨如何更好地进行创业,从市场分析到产品实现的各个环节。 深入市场洞察 在创业之前,深入了解目标市场是至关重要的。我们需要…...
C++ 红黑树模拟实现
💓博主CSDN主页:麻辣韭菜💓 ⏩专栏分类:C知识分享⏪ 🚚代码仓库:C高阶🚚 🌹关注我🫵带你学习更多C知识 🔝🔝 前言 前面我们实现了AVL树,发明AVL树…...
【数据结构】第三节:单链表
前言 本篇要求掌握的C语言基础知识:指针、结构体 目录 前言 单链表 概念 对比链表和顺序表 创建链表 实现单链表 准备工作 打印链表 创建节点并初始化 尾插 二级指针的调用 尾插代码 头插 尾删 头删 查找(返回节点) 在指定位…...
Python中操作Excel表对象并打包为脚本
一、准备工作 pip install pandas pip install openpyxl pip install pyinstaller 数据表格: 数据表下载 二、执行写入操作 import pandas as pd # pyinstaller --onefile attendance_records_score.py # 打包 # 读取源Excel文件(假设源表有列A…...
Python学习笔记23 - 目录操作
os模块操作目录相关函数 os.path模块操作目录相关函数 案例1 —— 列出指定目录下的所有.py文件 案例2 —— walk()...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
Python网页自动化Selenium中文文档
1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API,让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API,你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...
C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南 背景介绍完整操作步骤1. 创建Docker容器环境2. 验证GUI显示功能3. 安装ROS Noetic4. 配置环境变量5. 创建ROS节点(小球运动模拟)6. 配置RVIZ默认视图7. 创建启动脚本8. 运行可视化系统效果展示与交互技术解析ROS节点通…...
