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

文件分片上传

分片上传:

1、前端(vue2+elementui)

<template>
  <div>
    <el-upload
      :http-request="handleUpload"
      :before-upload="beforeUpload"
      multiple
      :auto-upload="false"
      :on-change="handleFileChange"
    >
      <el-button slot="trigger" type="primary">选择文件</el-button>
      <el-button type="success" @click="submitUpload" :disabled="!file">上传</el-button>
    </el-upload>
    <el-progress v-if="progress > 0" :percentage="progress" status="success"></el-progress>
  </div>
</template>

<script>
export default {
  data() {
    return {
      file: null, // 当前文件
      progress: 0, // 上传进度
      chunkSize: 5 * 1024 * 1024, // 每个分片大小 5MB
    };
  },
  methods: {
    // 文件选择回调
    handleFileChange(file) {
      this.file = file.raw;
    },

    // 上传前检查
    beforeUpload(file) {
      if (file.type !== "application/pdf") {
        this.$message.error("只能上传 PDF 文件!");
        return false;
      }
      if (file.size > 50 * 1024 * 1024) {
        this.$message.error("文件大小不能超过 50MB!");
        return false;
      }
      return true;
    },

    // 分片上传逻辑
    async handleUpload(option) {
      const file = option.file;
      const totalChunks = Math.ceil(file.size / this.chunkSize);
      let uploadedChunks = 0;

      for (let i = 0; i < totalChunks; i++) {
        const start = i * this.chunkSize;
        const end = Math.min(file.size, start + this.chunkSize);
        const chunk = file.slice(start, end);

        const formData = new FormData();
        formData.append("chunk", chunk);
        formData.append("fileName", file.name);
        formData.append("chunkIndex", i);
        formData.append("totalChunks", totalChunks);

        try {
          await this.$axios.post("/api/upload/chunk", formData);
          uploadedChunks++;
          this.progress = Math.round((uploadedChunks / totalChunks) * 100);
        } catch (error) {
          this.$message.error("上传失败,请重试!");
          console.error(error);
          return;
        }
      }

      // 通知服务器合并文件
      try {
        await this.$axios.post("/api/upload/merge", { fileName: file.name, totalChunks });
        this.$message.success("文件上传成功!");
      } catch (error) {
        this.$message.error("文件合并失败!");
        console.error(error);
      }
    },

    submitUpload() {
      if (this.file) {
        this.handleUpload({ file: this.file });
      } else {
        this.$message.error("请选择文件!");
      }
    },
  },
};
</script>

后端(.net6 webApi)

  1. 分片上传接口
    用于保存文件分片到临时目录:
[HttpPost("chunk")]
public async Task<IActionResult> UploadChunk([FromForm] IFormFile chunk, [FromForm] string fileName, [FromForm] int chunkIndex, [FromForm] int totalChunks)
{
    try
    {
        var tempFolder = Path.Combine("TempChunks", fileName);
        if (!Directory.Exists(tempFolder))
            Directory.CreateDirectory(tempFolder);

        var chunkPath = Path.Combine(tempFolder, $"{chunkIndex}");
        using (var stream = new FileStream(chunkPath, FileMode.Create))
        {
            await chunk.CopyToAsync(stream);
        }

        return Ok(new { success = true });
    }
    catch (Exception ex)
    {
        return BadRequest(new { success = false, message = ex.Message });
    }
}
  1. 文件合并接口
    用于将所有分片合并为一个完整文件:
[HttpPost("merge")]
public async Task<IActionResult> MergeFile([FromBody] MergeRequest mergeRequest)
{
    try
    {
        var tempFolder = Path.Combine("TempChunks", mergeRequest.FileName);
        if (!Directory.Exists(tempFolder))
            return BadRequest(new { success = false, message = "分片目录不存在" });

        var finalFolder = Path.Combine("Uploads");
        if (!Directory.Exists(finalFolder))
            Directory.CreateDirectory(finalFolder);

        var finalFilePath = Path.Combine(finalFolder, mergeRequest.FileName);
        using (var finalFileStream = new FileStream(finalFilePath, FileMode.Create))
        {
            for (int i = 0; i < mergeRequest.TotalChunks; i++)
            {
                var chunkPath = Path.Combine(tempFolder, $"{i}");
                if (!System.IO.File.Exists(chunkPath))
                    return BadRequest(new { success = false, message = $"缺少分片 {i}" });

                var chunkData = await System.IO.File.ReadAllBytesAsync(chunkPath);
                await finalFileStream.WriteAsync(chunkData, 0, chunkData.Length);

                System.IO.File.Delete(chunkPath); // 删除已合并的分片
            }
        }

        Directory.Delete(tempFolder); // 删除临时目录

        return Ok(new { success = true, path = $"/Uploads/{mergeRequest.FileName}" });
    }
    catch (Exception ex)
    {
        return BadRequest(new { success = false, message = ex.Message });
    }
}

public class MergeRequest
{
    public string FileName { get; set; }
    public int TotalChunks { get; set; }
}



http://www.kler.cn/a/407809.html

相关文章:

  • AMD(Xilinx) FPGA配置Flash大小选择
  • 深度学习每周学习总结J6(ResNeXt-50 算法实战与解析 - 猴痘识别)
  • 【Isaac Sim】配置 Nucleus 本地服务器
  • 20.100ASK_T113-PRO 开发板开机自动QT程序简单的方法一
  • uni-app 修改复选框checkbox选中后背景和字体颜色
  • 对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
  • ubuntu, 安装部署comfyui,记录2:下载模型GGuf及测试
  • 解锁 ChatGPT 超强交互:超级提示词的魔力
  • C#中的二维数组的应用:探索物理含义与数据结构的奇妙融合
  • I.MX6U 裸机开发15.IRQ中断——GPIO中断处理
  • 《FreeRTOS任务删除篇》
  • 第二十九章 TCP 客户端 服务器通信 - 记录的拼接
  • linux下i2c开发与框架源码分析
  • 如何利用java爬虫获得淘宝商品评论
  • 网络安全(骇客)—技术学习
  • 【案例分享】图表工具TeeChart在环境研究领域的数据可视化应用
  • vue前端下载某一区域为照片格式
  • leetcode - 1861. Rotating the Box
  • 后端接受大写参数(亲测能用)
  • Elasticsearch面试内容整理-安全与权限管理
  • 安卓InputDispatching Timeout ANR 流程
  • RocketMQ: 客户端使用指南
  • Canvas 前端艺术家
  • Ubuntu20.04 rk3588交叉编译opencv4.10
  • MySQL面试题补
  • DAY1 网络编程(TCP客户端服务器)