大文件分块上传

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

相关文章

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. 聊天输入组件改造功能特点使用说明注意事项

springboot上传zip包并解压至服务器nginx目录方式

《springboot上传zip包并解压至服务器nginx目录方式》:本文主要介绍springboot上传zip包并解压至服务器nginx目录方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录springboot上传zip包并解压至服务器nginx目录1.首先需要引入zip相关jar包2.然

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

Java实现数据库图片上传与存储功能

《Java实现数据库图片上传与存储功能》在现代的Web开发中,上传图片并将其存储在数据库中是常见的需求之一,本文将介绍如何通过Java实现图片上传,存储到数据库的完整过程,希望对大家有所帮助... 目录1. 项目结构2. 数据库表设计3. 实现图片上传功能3.1 文件上传控制器3.2 图片上传服务4. 实现

使用mvn deploy命令上传jar包的实现

《使用mvndeploy命令上传jar包的实现》本文介绍了使用mvndeploy:deploy-file命令将本地仓库中的JAR包重新发布到Maven私服,文中通过示例代码介绍的非常详细,对大家的学... 目录一、背景二、环境三、配置nexus上传账号四、执行deploy命令上传包1. 首先需要把本地仓中要

Java实现数据库图片上传功能详解

《Java实现数据库图片上传功能详解》这篇文章主要为大家详细介绍了如何使用Java实现数据库图片上传功能,包含从数据库拿图片传递前端渲染,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、前言2、数据库搭建&nbsChina编程p; 3、后端实现将图片存储进数据库4、后端实现从数据库取出图片给前端5、前端拿到