前端大文件分块上传、断点续传
文章目录
- 前端分块上传流程
- 分块上传代码
- 重点
- 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()...
今天你学langchain了吗?
langchain的重重难关 学习langchain也有一段时间了,从最初的0.0339版本到现在的稳定版本,langchain走了很长的路.在学习的路上也遇到了很多的困难. api_key难关 学习langchain最大的困难就是openai的API_KEY,国内无法申请到官方账号,申请到了也无法进行充值.好在有几美元的免…...
插值算法-代码实现
1、 import java.util.HashMap; import java.util.Map;public class Interpolation {public static void main(String[] args) {// 定义给定的 XML 字段值Map<String, double[]> xmlValues new HashMap<>();xmlValues.put("faceSize", new double[]{10…...
113.PyQt5_QtPrintSupport_打印操作
我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈 入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈 虚 拟 环 境 搭 建 :👉&…...
在vue中使用bing map 的小demo
1.注意事项(关于经纬度) 如果不转换成WGS84 标准的经纬度 bing map会报错 如果要在 Bing Maps 中使用中国地区的经纬度,需要先将其转换为 WGS84 标准的经纬度。你可以使用第三方的坐标转换服务,或者使用相关的 JavaScript 库进行…...
基于uni-app的埋点sdk设计
一、统计app激活状态 在App.vue 中 利用onShow生命周期验证 或者操作 onShow: function () { uni.showToast({ title: onShow }) }, 二、页面级别的统计 (进入页面、停留时长、手机系统信息、网络状态、页面路径、标题) 需要收集的数据 { &quo…...
Python学习笔记(三)
一、使用朴素贝叶斯制作鸢尾花数据模型 from sklearn.preprocessing import StandardScaler from sklearn.naive_bayes import MultinomialNB from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.feature_extraction…...
Python办公自动化之Excel做表自动化:全网最全,看这一篇就够了!
0 Python Excel库对比 我们先来看一下python中能操作Excel的库对比(一共九个库): 1 Python xlrd 读取 操作Excel 1.1 xlrd模块介绍 (1)什么是xlrd模块? python操作excel主要用到xlrd和xlwt这两个库&…...
【学习笔记】R语言入门与数据分析1
数据分析 数据分析的过程: 数据采集 数据存储 数据分析 数据挖掘 数据可视化 进行决策 数据挖掘 数据量大 复杂度高,容忍一定的误差限 追求相关性而非因果性 数据可视化 直观明了 R语言介绍 R是免费的(开源软件、扩展性好)…...
MyBatis-Spring整合
引入Spring之前需要了解mybatis-spring包中的一些重要类; http://www.mybatis.org/spring/zh/index.html 什么是 MyBatis-Spring? MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。 知识基础 在开始使用 MyBatis-Spring 之前&#x…...
资深亚马逊运营实战技巧:跨境电商6大选品法
1、工具选品法 比如店雷达, 通过大数据分析工具选出来利基产品或者通过工具选出来利基的市场,然后再通过分析市场来得到产品。 以女装为例,通过大数据分析,全方位对市场需求、款式、质量等进行多维度判断,其中SKU销量…...
videopro wordpress/百度如何发布作品
Q:我用outlook下载了邮箱里所有的邮件,现在想刻盘保存,我应该保存哪些文件呢,以保证我把光盘内的数据放到其他电脑上还可以用outlook查看我的这些邮件? A:找到你outlook的按装文件,你可以全部刻下来,到后期…...
建设网站大概要花多少钱/百度网盘网页版入口官网
1.应用场景 主要用于推荐一些架构师应该阅读的优秀书籍。 2.学习/操作 1.文档阅读 架构师必读书单 | “华仔,放学别走!” 第5期-极客时间 2.整理输出 原文 你好,我是华仔。 在专栏更新的时候,很多同学留言希望我推荐一些书籍可以…...
有没有兼职做设计的网站吗/网站seo优化是什么意思
VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。 DTO(Data Transfer Object):数据传输对象,这个概…...
网站开发模式分为/长春seo网站排名
06-9-27在网吧里,任何时间都会有人在吃饭,有人在睡觉,有人在玩。不论是清晨,中午,还是深夜,都有一些人挺不住了,他们用疲惫无神的双眼仔细注视着屏幕,仿佛创作中的马克思。他们是铁人…...
局域网 手机网站建设/百度竞价推广效果怎么样
找到mysql的安装目录的bin/myisamchk工具,在命令行中输入:myisamchk -c -r ../data/tablename/posts.MYI然后myisamchk 工具会帮助你恢复数据表的索引。好象也不用重新启动mysql,问题就解决了。当你试图修复一个被破坏的表的问题时࿰…...
找老外做网站可靠吗/cms建站系统
f{} Python3.6及以上版本才有,Python3.5及以下版本会出错。 %s%() 常用格式 {}.format() {}里面可以带数字也可以不带数字,带数字编号可以调换输出顺序,还可以带关键字。 device torch.device(fcuda:{opt.local_rank}) #python3.6 device …...