前端使用 spark-md5 实现大文件切片上传

需要计算文件MD5和、分片MD5:

封装公共方法代码如下:

javascript 复制代码
import SparkMD5 from "spark-md5"

/**
 * 计算文件MD5
 * @param file
 * @returns
 */
export function calculateFileMD5(file) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = function (e) {
      const spark = new SparkMD5.ArrayBuffer();
      spark.append(e.target.result);
      resolve(spark.end());
    };
    reader.readAsArrayBuffer(file);
  })
}

/**
 * 计算分片MD5
 * @param chunk 当前切切片
 * @returns
 */
export function calculateChunkMD5(chunk) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = function (e) {
      const spark = new SparkMD5.ArrayBuffer();
      spark.append(e.target.result);
      resolve(spark.end());
    };
    reader.readAsArrayBuffer(chunk);
  })
}

在上传页面调用:

javascript 复制代码
<template>
  <form id="uploadForm">
    <div class="fileBox">
      <span class="fileLabel">文件:&nbsp; </span>
      <input type="file" id="file" name="file">
    </div>
    <el-progress id="progressBar" :percentage="progress" style="width: 335px;"></el-progress>
    <button type="submit">提交</button>
  </form>
</template>

<script setup>
import { calculateFileMD5, calculateChunkMD5 } from './calculateMd5'
import { ElMessage, ElMessageBox } from 'element-plus'
import axios from 'axios'
import { getToken } from "@/utils/auth";
const emit = defineEmits(['send-upload'])
const uploadChunkUrl = ref(import.meta.env.VITE_APP_BASE_API + "/dbms/file/chunk"); //上传接口
const mergeUrl = ref(import.meta.env.VITE_APP_BASE_API + "/dbms/file/merge"); //合并接口

const file = ref(null)
const progress = ref(0)

async function uploadFile(file) {
  const chunkSize = 5 * 1024 * 1024; // 5MB每片
  const totalChunks = Math.ceil(file.size / chunkSize);
  const fileMd5 = await calculateFileMD5(file); // 计算整个文件的MD5

  for (let i = 0; i < totalChunks; i++) {
    const start = i * chunkSize;
    const end = Math.min(file.size, start + chunkSize);
    const chunk = file.slice(start, end);
    const chunkMd5 = await calculateChunkMD5(chunk); // 计算当前分片的MD5
    progress.value = Math.round((i / totalChunks) * 100) // 计算上传进度

    const formData = new FormData();
    
    formData.append('file', chunk);
    formData.append('chunkNumber', i + 1);
    formData.append('totalChunks', totalChunks);
    formData.append('fileMd5', fileMd5);
    formData.append('chunkMd5', chunkMd5);
    formData.append('fileName', file.name);

    await axios.post(uploadChunkUrl.value, formData, {
      headers: { 'Content-Type': 'multipart/form-data', 'Authorization': 'Bearer ' + getToken() }
    });
  }

  // 所有分片上传完成后,通知后端合并
  const res = await axios.post(mergeUrl.value, {
    fileMd5: fileMd5,
    fileName: file.name,
    totalChunks: totalChunks
  });
  // console.log(res)
  if (res.status == 200) {
    progress.value = 100
    ElMessage.success('文件上传成功!')
    // 上传成功 通知父组件关闭弹窗,清空下拉表单,更新列表数据
    emit('send-upload')
  }
}

onMounted(async () => {
  /**触发上传**/
  document.getElementById("uploadForm").onsubmit = async function (event) {
    event.preventDefault();
    
    const fileInput = document.getElementById("file");
    file.value = fileInput.files[0];
    if (!file.value) {
      ElMessage.warning("请选择文件!")
      return;
    }
    uploadFile(file.value)
  }
})
</script>
相关推荐
浮桥24 分钟前
vue3实现pdf文件预览 - vue-pdf-embed
前端·vue.js·pdf
七夜zippoe36 分钟前
前端开发中的难题及解决方案
前端·问题
Hockor2 小时前
用 Kimi K2 写前端是一种什么体验?还支持 Claude Code 接入?
前端
杨进军2 小时前
React 实现 useMemo
前端·react.js·前端框架
萤火虫儿飞飞2 小时前
从基础加热到智能生态跨越:艾芬达用创新重构行业价值边界!
大数据·人工智能·重构
seanmeng20222 小时前
Apache Iceberg on AWS - 通过Firehose流式导入数据到Iceberg表
大数据
海底火旺2 小时前
浏览器渲染全过程解析
前端·javascript·浏览器
你听得到112 小时前
揭秘Flutter图片编辑器核心技术:从状态驱动架构到高保真图像处理
android·前端·flutter
驴肉板烧凤梨牛肉堡2 小时前
浏览器是否支持webp图像的判断
前端
Xi-Xu2 小时前
隆重介绍 Xget for Chrome:您的终极下载加速器
前端·网络·chrome·经验分享·github