大文件分块上传

2024-05-10 03:28
文章标签 上传 分块

本文主要是介绍大文件分块上传,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

断点续传

断点续传需要为每个分块加md5值,如果用户取消上传,可以知道那些分块已经上传了

切块上传

只要校验整个文件的完整性就好

前端代码示例

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>大文件上传</title><style>body {padding: 24px;}button {padding: 8px 16px;cursor: pointer;border: 1px solid #409eff;border-radius: 4px;background-color: #409eff;color: #fff;margin-right: 16px;}.progress {width: 100%;height: 24px;background-color: #ebeef5;border-radius: 12px;margin-bottom: 24px;overflow: hidden;}.progress-inner {width: 0%;height: 100%;background-color: #67c23a;border-radius: 12px;text-align: right;transition: width 0.6s ease;}.progress-text {color: #fff;margin: 0 5px;display: inline-block;font-size: 12px;}</style></head><body><!-- 进度条 --><div class="progress"><div class="progress-inner"><div class="progress-text"></div></div></div><!-- 上传完显示预览地址 --><p><a id="filelink"></a></p><!-- 操作按钮 --><button id="upload"><input type="file" id="file" style="display: none" />上传文件</button><button id="download">下载文件</button><script src="https://unpkg.com/axios/dist/axios.min.js"></script><script src="https://unpkg.com/spark-md5@3.0.2/spark-md5.js"></script><script>// 上传document.querySelector("#upload").addEventListener("click", () => {document.querySelector("#file").click();});// 选中文件直接上传document.querySelector("#file").addEventListener("change", async (e) => {const file = e.target.files[0];// 获取文件的所有分块const blobList = getFileSlices(file);const uploadRequests = [];// 所有分块的md5值// 后端根据md5值存的块,请求合并根据md5值进行合并const md5ChunkList = [];let totalUploaded = 0;for (let i = 0; i < blobList.length; i++) {const blob = blobList[i];const md5 = await calculateMD5(blob);md5ChunkList.push(md5);const formData = new FormData();formData.append("file", blob);formData.append("md5", md5);const request = axios({method: "post",url: "http://localhost:3000/upload",data: formData,onUploadProgress: (progressEvent) => {totalUploaded += progressEvent.loaded;let percentCompleted = Math.floor((totalUploaded / file.size) * 100);// 为合并留1%进度条if (percentCompleted >= 100) {percentCompleted = 99;}updateProgressBar(percentCompleted);},});uploadRequests.push(request);}try {// 等待所有分块上传完成,再请求合并await Promise.all(uploadRequests);const { data } = await axios({method: "post",url: "http://localhost:3000/merge",data: { md5: md5ChunkList, fileName: file.name },});updateProgressBar(100);const url = `http://localhost:3000${data.data}`;console.log(url);const a = document.querySelector("#filelink");a.href = url;a.target = "_blank";a.innerText = url;} catch (e) {console.error(e);}});// 获取文件分块// 默认分块大小5mfunction getFileSlices(file, segmentSize = 5 * 1024 * 1024) {const fileSlices = [];let offset = 0;while (offset < file.size) {const segment = file.slice(offset, offset + segmentSize);fileSlices.push(segment);offset += segmentSize;}return fileSlices;}// 计算分块的md5值function calculateMD5(blob) {return new Promise((resolve, reject) => {const fileReader = new FileReader();let spark = new SparkMD5.ArrayBuffer();fileReader.onload = (e) => {spark.append(e.target.result);resolve(spark.end());};fileReader.onerror = () => {reject("blob读取失败");};fileReader.readAsArrayBuffer(blob);});}function updateProgressBar(percent) {const width = percent + "%";document.querySelector(".progress-inner").style.width = width;document.querySelector(".progress-text").innerText = width;}// 下载document.querySelector("#download").addEventListener("click", () => {const url = document.querySelector("#filelink").innerText;const fileName = url.substring(url.lastIndexOf("/") + 1);const download = document.createElement("a");download.href = `http://localhost:3000/download/${fileName}`;download.download = fileName;download.target = "_blank";download.click();});</script></body>
</html>

后端代码示例

import express from "express";
import cors from "cors";
import multer from "multer";
import fs from "fs";
import path from "path";
import mime from "mime-types";const app = express();
const port = 3000;
const upload = multer({ dest: "uploads/" });// 处理跨域请求
app.use(cors());
// 解析客户端请求中的JSON数据,解析后的对象将被添加到req.body
app.use(express.json());
// 启用对静态文件的服务
app.use("/uploads", express.static("uploads"));app.get("/", (req, res) => {res.send("Hello World!");
});// 上传
app.post("/upload", upload.single("file"), (req, res) => {const file = req.file;const md5 = req.body.md5;const newPath = path.join(path.dirname(file.path), md5);fs.renameSync(file.path, newPath);res.send({ code: 200 });
});// 合并
app.post("/merge", async (req, res) => {const { md5, fileName } = req.body;await concatFiles(md5, fileName);res.send({ code: 200, msg: "合并成功", data: `/uploads/${fileName}` });
});// 下载文件流
app.get("/download/:fileName", (req, res) => {const { fileName } = req.params;const file = path.join("./uploads", fileName);const type = mime.lookup(file);res.setHeader("Content-disposition","attachment; filename=" + encodeURIComponent(path.basename(file)));res.setHeader("Content-type", type);const filestream = fs.createReadStream(file);filestream.pipe(res);
});app.listen(port, () => {console.log(`listening on port ${port}`);
});// 合并切片的文件
async function concatFiles(fileChunks, name) {const filePath = path.join("./uploads", name);const writeStream = fs.createWriteStream(filePath);for (let i = 0; i < fileChunks.length; i++) {const chunkPath = path.join("./uploads", fileChunks[i]);const file = fs.createReadStream(chunkPath);file.pipe(writeStream, { end: false });// 等待当前文件读完再进行下一个await new Promise((resolve) => file.on("end", resolve));fs.unlink(chunkPath, (e) => {if (e) {console.error("删除文件切片失败", e);}});}writeStream.end();
}

参考

面试官:如何实现大文件上传
大文件分块上传

这篇关于大文件分块上传的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/975300

相关文章

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

Python一次性将指定版本所有包上传PyPI镜像解决方案

《Python一次性将指定版本所有包上传PyPI镜像解决方案》本文主要介绍了一个安全、完整、可离线部署的解决方案,用于一次性准备指定Python版本的所有包,然后导出到内网环境,感兴趣的小伙伴可以跟随... 目录为什么需要这个方案完整解决方案1. 项目目录结构2. 创建智能下载脚本3. 创建包清单生成脚本4

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建

GitLab文件的上传与下载方式

《GitLab文件的上传与下载方式》:本文主要介绍GitLab文件的上传与下载方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录GitLab 项目拉取到本地GitLab 项目上传方法方法 1:本地项目未初始化Git方法 2:本地项目已初始化GitGitLab 上

Nginx 413修改上传文件大小限制的方法详解

《Nginx413修改上传文件大小限制的方法详解》在使用Nginx作为Web服务器时,有时会遇到客户端尝试上传大文件时返回​​413RequestEntityTooLarge​​... 目录1. 理解 ​​413 Request Entity Too Large​​ 错误2. 修改 Nginx 配置2.1

Java应用如何防止恶意文件上传

《Java应用如何防止恶意文件上传》恶意文件上传可能导致服务器被入侵,数据泄露甚至服务瘫痪,因此我们必须采取全面且有效的防范措施来保护Java应用的安全,下面我们就来看看具体的实现方法吧... 目录恶意文件上传的潜在风险常见的恶意文件上传手段防范恶意文件上传的关键策略严格验证文件类型检查文件内容控制文件存储

Java实现MinIO文件上传的加解密操作

《Java实现MinIO文件上传的加解密操作》在云存储场景中,数据安全是核心需求之一,MinIO作为高性能对象存储服务,支持通过客户端加密(CSE)在数据上传前完成加密,下面我们来看看如何通过Java... 目录一、背景与需求二、技术选型与原理1. 加密方案对比2. 核心算法选择三、完整代码实现1. 加密上

在React聊天应用中实现图片上传功能

《在React聊天应用中实现图片上传功能》在现代聊天应用中,除了文字和表情,图片分享也是一个重要的功能,本文将详细介绍如何在基于React的聊天应用中实现图片上传和预览功能,感兴趣的小伙伴跟着小编一起... 目录技术栈实现步骤1. 消息组件改造2. 图片预览组件3. 聊天输入组件改造功能特点使用说明注意事项