当前位置: 首页 > news >正文

切片上传version2.0 进度用模拟后端实时返回的进度,大文件1.05GB耗时7.49秒

前端文件

// 1 安装依库 npm install spark-md5 --save
// 2 引入 import SparkMD5 from "spark-md5";
<template><div class="upload-container"><input type="file" @change="handleFileChange" /><button @click="upload" :disabled="!file">上传</button><div v-if="progress > 0" class="progress">上传进度:{{ progress.toFixed(2) }}%</div><!-- 全屏 Loading 遮罩 --><div v-if="loading" class="loading-overlay"><div class="spinner"></div><p>文件上传中,请稍候...</p></div></div>
</template><script setup>
import { ref } from "vue";
import SparkMD5 from "spark-md5";
// 1 安装依库 npm install spark-md5 --save
// 2 引入 import SparkMD5 from "spark-md5";
const file = ref(null);
const progress = ref(0);
const loading = ref(false);
const chunkSize = 3 * 1024 * 1024; // 每片3MBfunction handleFileChange(e) {progress.value = 0;console.log("🚀 ~ handleFileChange ~ e:", e.target.files)file.value = e.target.files[0];console.log("🚀 ~ handleFileChange ~ file.value :", file.value)
}// 上传主流程
async function upload() {if (!file.value) return;loading.value = true; // 打开全屏遮罩const fileSize = file.value.size;const chunkCount = Math.ceil(fileSize / chunkSize);const chunkAll = [];// 生成分片for (let i = 0; i < chunkCount; i++) {const start = i * chunkSize;const end = Math.min(fileSize, start + chunkSize);const blob = file.value.slice(start, end);chunkAll.push({ index: i, blob });}// 计算整文件MD5(这里只是示例)const fullMd5 = await calculateFileMD5(file.value);// 逐片上传并实时更新进度for (let i = 0; i < chunkAll.length; i++) {const formData = new FormData();formData.append("chunk", chunkAll[i].blob, `chunk${i}`);formData.append("index", String(i));formData.append("chunkCount", String(chunkCount));formData.append("fileSize", Number(fileSize));// 最后一片加上 MD5 表示结束if (i === chunkAll.length - 1) {formData.append("MD5", fullMd5);}const res = await fetch("http://localhost:3000/upload", {method: "POST",body: formData,});const result = await res.json();loading.value = false; // 上传完成关闭遮罩console.log("🚀 ~ 后端返回 ~ result:", result)// 根据后端返回的 progress 更新if (result.progress !== undefined) {progress.value = result.progress;}if (result.isLast) {progress.value = 100;alert("上传完成!");}}
}
// 计算整文件MD5
function calculateFileMD5(file) {return new Promise((resolve) => {const reader = new FileReader();const spark = new SparkMD5.ArrayBuffer();reader.readAsArrayBuffer(file);reader.onload = (e) => {spark.append(e.target.result);resolve(spark.end());};});
}
</script><style scoped>
.upload-container {padding: 20px;
}button {margin-left: 10px;padding: 5px 10px;
}.progress {margin-top: 10px;color: #2c7a7b;
}/* 全屏遮罩样式 */
.loading-overlay {position: fixed;top: 0;left: 0;width: 100vw;height: 100vh;background-color: rgba(0, 0, 0, 0.6);display: flex;flex-direction: column;justify-content: center;align-items: center;color: #fff;font-size: 18px;z-index: 9999;
}/* 动画 */
.spinner {border: 6px solid rgba(255, 255, 255, 0.2);border-top: 6px solid #fff;border-radius: 50%;width: 50px;height: 50px;animation: spin 0.9s linear infinite;margin-bottom: 16px;
}@keyframes spin {to {transform: rotate(360deg);}
}
</style>

后端文件

模拟后端大文件分片上传服务

1pnpm init -y
2pnpm install express multer cors
3  打开终端 cmd node启动 node bigfileserver.js

/* 
模拟后端大文件分片上传服务
1pnpm init -y
2pnpm install express multer cors
3  打开终端 cmd node启动 node bigfileserver.jsexpress:Web 框架multer:处理 multipart/form-data(文件上传)cors:允许前端跨域请求
*/
import express from "express";
import multer from "multer";
import cors from "cors";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";// __dirname 在 ES Module 下需要自己定义
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);const app = express();
const PORT = 3000;app.use(cors());// 设置上传目录
const upload = multer({ dest: "uploads/" });// 用于记录上传开始时间
let startTime = null;
function formatFileSize(bytes) {if (bytes < 1024) return bytes + " B";else if (bytes < 1024 ** 2) return (bytes / 1024).toFixed(2) + " KB";else if (bytes < 1024 ** 3) return (bytes / 1024 / 1024).toFixed(2) + " MB";else return (bytes / 1024 / 1024 / 1024).toFixed(2) + " GB";
}app.post("/upload", upload.single("chunk"), (req, res) => {if (!startTime) startTime = Date.now();const chunk = req.file;const index = Number(req.body.index);      // 前端传递当前分片索引const chunkCount = Number(req.body.chunkCount); // 前端传总分片数const fileSize = Number(req.body.fileSize);const targetPath = path.join(__dirname, "uploads", chunk.originalname);fs.renameSync(chunk.path, targetPath); // 保存分片console.log(`分片 ${index + 1}/${chunkCount} 保存成功,大小: ${formatFileSize(chunk.size)}`);// 返回当前进度给前端const progress = ((index + 1) / chunkCount) * 100; const isLast = req.body.MD5 ? true : false;if (isLast) {const endTime = Date.now();console.log(`上传完成,总耗时: ${((endTime - startTime)/1000).toFixed(2)} 秒`);console.log(`上传完成,总大小: ${formatFileSize(fileSize)}`);startTime = null;}res.json({ code: 0, msg: "分片上传成功", progress, isLast });
});app.listen(PORT, () => {console.log(`Server running on http://localhost:${PORT}`);
});

http://www.dtcms.com/a/582439.html

相关文章:

  • App HTTPS 抓包 工程化排查与工具组合实战
  • 分页条初始化
  • 网站做seo的好处京东网上购物官方网站
  • 网站的风格设计下载了wordpress然后怎么用
  • 网站开发用的开源系统龙华在深圳算什么档次
  • 正确使用玛伐凯泰(Mavacamton)治疗梗阻性肥厚型心肌病的剂量间隔
  • ViDoRAG详解:多模态文档检索增强生成框架的革命性突破
  • 玉溪做网站建设的公司昆明网站设计都需要设计什么
  • 计算机关于网站开发的证书关于申请网站建设的报告
  • 获取DPI、设置进程DPI感知(C++源码)
  • 时间序列图的“性能陷阱”:Highcharts “金融级”优化方案
  • 网站开发的方法和步骤网站构成的作用是什么
  • 6、prometheus资源规划
  • 淄博哪有做网站的wordpress无头像
  • 在 DigitalOcean GPU 云服务上使用 LangChain 构建Serverless AI 应用
  • 【生活技术分享】基于“稀释-增香”原理的波特酒风味优化方案
  • 如何做国外假发网站优秀的图片设计网站
  • C++笔记-23-类和对象-多态
  • 网站开发有哪些方向微信小程序开通要钱吗
  • 网站开发技术架构南京网站设计平台
  • CSS 导航栏
  • html5 网站正在建设中网页设计 html
  • 拓扑排序深入
  • docker部署kafka
  • 【镜中异客:AI与人类的禁忌之舞】
  • 微信网站模版下载新闻类网站源码
  • 手机网站滑动效果深圳一公司今年成立16家核检机构
  • 面向强化学习的状态空间建模:RSSM和PyTorch(3)
  • #Prometheus 权威指南:云原生监控的技术架构与实践精髓
  • Android11-Launcher3 定制-去除副屏幕-可以滑动效果