大文件分片上传实战:前端实现
前言
随着互联网应用的发展,大文件上传成为了许多应用不可或缺的功能之一。传统的文件上传方式对于大文件来说效率低下,直接将整个文件发送到服务器可能会导致内存溢出或网络阻塞等问题,容易出现上传失败的情况。为此,一种更为高效、可靠的解决方案——分片上传应运而生。本文将详细介绍如何使用前端 JavaScript 技术实现大文件分片上传,并通过一个示例代码来展示其实现过程,并介绍使用 JavaScript 来实现大文件分片上传的基本原理、逻辑和思路。
实现原理
- 文件切片:将一个大文件分成多个小块(分片)进行上传。
- 并发上传:可以同时上传多个分片,提高上传速度。
- 错误重试:如果某个分片上传失败,可以单独重试该分片而不需要重新上传整个文件。
- 合并分片:所有分片上传完成后,在服务器端将这些分片合并成原始文件。
实现逻辑
以下是实现大文件分片上传的一些关键步骤:
-
初始化上传:
- 用户选择要上传的文件。
- 获取文件大小和名称等信息。
-
计算分片:
- 根据预设的分片大小计算需要分割的分片数量。
- 使用
File.slice()
方法切割文件。
-
分片上传:
- 使用 Ajax(通常是 XMLHttpRequest 或 Fetch API)逐个上传每个分片。
- 可以设置并发上传的数量,以平衡网络负载。
-
进度监控:
- 监控每个分片的上传进度,并更新UI显示整体上传进度。
-
上传完成:
- 所有分片上传成功后,通知服务器进行分片合并。
- 如果服务器支持,也可以在客户端发送合并请求。
实现思路
- 前端:主要负责文件分片、分片上传以及进度显示等功能。
- 后端:接收分片并存储临时文件,提供接口用于最后合并分片为完整的文件。
本文仅针对前端板块进行探讨,后端实现也比较轻松,感兴趣的小伙伴可自行进行尝试或评论区找我询问代码,感谢支持。
示例代码
HTML 代码模块
<body>
<input type="file" id="input">
<button id="btn">上传</button>
</body>
样式展示
HTML元素绑定:
input
和btn
是HTML页面上的两个元素,input
通常是一个文件输入框,btn
是一个按钮。
(本文主要以探讨如何实现切片上传大文件这个功能为主,所以 HTML 模块较为简陋,敬请谅解,大家可根据自己的需求进行更改)
JS 代码模块
<script>
const input = document.getElementById("input")
const btn = document.getElementById("btn")
input.addEventListener("change", handleFileChange)
btn.addEventListener("click", handleUpload)
let fileObj = null
// 读取本地文件
function handleFileChange(e) {
console.log(e.target.files);
const [file] = e.target.files
fileObj = file
}
// 上传文件
function handleUpload() {
if (!fileObj) return
const chunkList = createChunk(fileObj)
const chunks = chunkList.map(({file}, index) => {
return {
file,
size: file.size,
percent: 0,
chunkName: `${fileObj.name}-${index}`,
fileName: fileObj.name,
index
}
})
// 发请求
uploadChunks(chunks)
}
// 切片
function createChunk(file, size = 5 * 1024 * 1024) {
const chunkList = []
let cur = 0
while(cur < file.size) {
// slice 切割
chunkList.push({file: file.slice(cur, cur + size)})
cur += size
}
return chunkList
}
// 请求
function uploadChunks(chunks) {
// 这个数组中的元素是对象,对象中有 Blob类型的文件对象
const formChunks = chunks.map(({file, fileName, index, chunkName}) => {
const formData = new FormData()
formData.append('file', file)
formData.append('fileName', fileName)
formData.append('chunkName', chunkName)
return {formData, index}
})
// 转换为后端能识别的类型,并依次发送片段
const requestList = formChunks.map(({ formData, index }) => {
return axios.post('http://localhost:3000/upload', formData, () => {
})
})
Promise.all(requestList).then(res => {
console.log(res, '所有的片段都传输成功')
mergeChunks()
})
}
</script>
这段代码是一个JavaScript
脚本,用于实现文件的分块上传以及进度跟踪。下面是针对这段代码的分析、思路以及相关知识点的讲解。
分析
-
监听事件:
-
当用户选择文件后,
handleFileChange
函数会被触发来处理文件选择事件,通过监听input
的change
事件来获取文件对象,并将其存储在fileObj
变量中。 -
当用户点击按钮时,
handleUpload
函数会被触发来处理文件上传事件。
-
-
文件切片:
- 大文件在上传前需要被分割成多个小块,这样可以避免因为网络不稳定而导致整个文件需要重新上传的问题。通过
createChunk
函数,使用slice
方法将文件分成多个Blob
类型的分片。每个分片的大小默认设定为 5MB,当然,我们可以根据自己的需求调整这个值。
- 大文件在上传前需要被分割成多个小块,这样可以避免因为网络不稳定而导致整个文件需要重新上传的问题。通过
-
构建 FormData:
- 因为后端无法接受
Blob
类型的数据,所以我们需要将Blob
格式的数据转换为FormData
格式的数据,并将每个分片通过FormData
对象进行封装,便于发送到服务器。这里,我们将分片名称、文件名称等信息也一起发送过去,以便服务器知道如何处理这些分片。
- 因为后端无法接受
-
发送请求:
- 使用
axios.post
方法向服务器发送文件块,并监听上传进度index
。 - 使用
Promise.all
来等待所有文件块上传完毕后,再进行下一步操作。(并发上传: 利用Promise.all
同时上传多个文件块,可以提高上传效率)
- 使用
-
合并文件块:
- 所有分片上传成功后,通过
mergeChunks
函数通知服务器进行合并操作。服务器收到请求后,会将所有分片重新组合成原始文件。
- 所有分片上传成功后,通过
知识点
-
DOM操作:通过
getElementById
获取 DOM 元素并添加事件监听器。 -
File API:使用
File
接口提供的slice
方法来切割文件。 -
FormData:用于构建 HTTP 请求体的数据结构,支持自动设置
Content-Type
头部。 -
Axios:一个基于
Promise
的 HTTP 客户端,用于浏览器和 node.js。 -
Promise.all:等待所有的
Promise
都完成(resolve 或 reject)。 -
异步编程:使用回调函数和 Promise 来处理异步任务,如文件上传和合并。
结论
本文通过一个简单的示例代码介绍了如何在前端实现大文件的分片上传。大文件分片上传是一项实用的技术,尤其在处理多媒体文件上传时显得尤为重要。分片上传不仅提高了上传效率,也增加了上传的可靠性。在实际应用中,还需要考虑更多的异常处理、错误重试机制等来完善这一功能。希望本文能够帮助读者理解和掌握大文件分片上传的基本原理和技术实现。