【.net core使用minio大文件分片上传】.net core使用minio大文件分片上传以及断点续传、秒传思路

news2024/11/26 12:52:47

版本:.net core 7
需求:net限制了上传的大小,只能上传25M上下的文件,如果上传一个八十多兆的文件,swagger接口报错,如果前端调用上传接口,会报CORS跨域错误,这篇文章介绍怎么使用分片的方式上传到minio后合并。

1.安装AWSSDK.S3的SDK

miniio 的分片上传封装,采用了AWSSDK.S3的SDK,该SDK是兼容了亚马逊s3的接口api,而minio是采用亚马逊s3的api模式
在这里插入图片描述

2.带上我写的后端MinioHelper.cs文件的封装方法

在这里插入图片描述

ConfigConstant.MinioBucketName是minio的存储桶名称,这里示例值是micro-element
ConfigConstant.MinioUserName是minio的AccessKey,值以你自己minio服务器的AccessKey为准
ConfigConstant.MinioPassWord是minio的SecretKey,值以你自己minio服务器的SecretKey为准
ConfigConstant.MinioAddressIp是minio的地址,需要带http://ip:port,比如http://192.168.110.11/

/// <summary>
/// miniio 的分片上传封装,采用了AWSSDK.S3的SDK,该SDK是兼容了亚马逊s3的接口api,而minio是采用亚马逊s3的api模式
/// </summary>
public class MinioHelper
{

    private  AmazonS3Client amazonS3Client;

    public MinioHelper()
    {
        AmazonS3Config config = new AmazonS3Config()
        {
            ServiceURL = ConfigConstant.MinioAddressIp  //地址采用服务器minio的http://ip:port
        };
        amazonS3Client = new AmazonS3Client(ConfigConstant.MinioUserName, ConfigConstant.MinioPassWord, config);//minio的账号密码
    }
    /// <summary>
    /// 初始化文件获取uploadid
    /// </summary>
    /// <param name="BucketName">桶名称</param>
    /// <param name="KeyName">存储文件的桶路径</param>
    /// <returns>返回uploadID</returns>
    public async   Task<string> InitMultipartChunkUploadFile(string BucketName,string KeyName) {
        InitiateMultipartUploadRequest initRequest=  new InitiateMultipartUploadRequest
        {
            BucketName = BucketName,
            Key = KeyName,
        };
        InitiateMultipartUploadResponse initResponse = await amazonS3Client.InitiateMultipartUploadAsync(initRequest);
        return  initResponse.UploadId;
    }
    /// <summary>
    /// 上传分片,这是看minio web控制台是看不出来分片文件的,需要去minio存储文件夹下的.minio.sys/multipart查看
    /// </summary>
    /// <param name="BucketName">桶名称</param>
    /// <param name="KeyName">存储文件的桶路径</param>
    /// <param name="UploadId">uploadID</param>
    /// <param name="ChunkCount">当前第几片</param>
    /// <param name="PartSize">当前分片大小</param>
    /// <param name="ChunkFile">分片文件</param>
    /// <returns>返回etag和当前第几片</returns>
    public async  Task<PartETag> ChunkUploadAsync(string BucketName, string KeyName,string UploadId, int ChunkCount, long PartSize, Stream ChunkFile) {
        UploadPartRequest uploadRequest = new UploadPartRequest();
        uploadRequest.BucketName = BucketName;
        uploadRequest.Key = KeyName;
        uploadRequest.UploadId = UploadId;
        uploadRequest.PartNumber = ChunkCount;
        uploadRequest.InputStream = ChunkFile;
        uploadRequest.PartSize = PartSize;
        //进行分片上传
        UploadPartResponse up1Response = await amazonS3Client.UploadPartAsync(uploadRequest);
        return  new PartETag { ETag = up1Response.ETag, PartNumber = ChunkCount };
    }
    /// <summary>
    /// 合并分片为整个文件,合并后minio存储的文件夹里.minio.sys/multipart下该文件会删除
    /// </summary>
    /// <param name="BucketName">桶名称</param>
    /// <param name="KeyName">存储文件的桶路径</param>
    /// <param name="UploadId">uploadID</param>
    /// <param name="partETags">当前第几片</param>
    /// <returns>返回存储的Key属性字段的相对路径(location是绝对路径,该属性不可取,如果服务器迁移会使人崩溃!)</returns>

    public async  Task<CompleteMultipartUploadResponse> CompleteMultipartUploadFile(string BucketName, string KeyName, string UploadId, List<PartETag> partETags) {
        CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest
        {
            BucketName = BucketName,
            Key = KeyName,
            UploadId = UploadId,
            PartETags = partETags,
        };
        CompleteMultipartUploadResponse compResponse = await amazonS3Client.CompleteMultipartUploadAsync(compRequest);
        return compResponse;
    }

    /// <summary>
    /// 查询该uploadID已上传了几个分片详细信息
    /// </summary>
    /// <param name="BucketName">桶名称</param>
    /// <param name="KeyName">存储文件的桶路径</param>
    /// <param name="UploadId">uploadID</param>
    /// <returns>只需要返回值数组下每个对象里的eTag和partNumber</returns>
    public async  Task<List<PartDetail>> ListPartsUploadInfo(string BucketName, string KeyName, string UploadId) {
        ListPartsRequest listPartRequest = new ListPartsRequest
        {
            BucketName = BucketName,
            Key = KeyName,
            UploadId = UploadId
        };
        ListPartsResponse listPartResponse = await amazonS3Client.ListPartsAsync(listPartRequest);
        return listPartResponse.Parts;
    }
}

3.在需要使用的地方引入该项目

在这里插入图片描述

4.申明MinioHelper文件的构造函数

在这里插入图片描述

        private MinioHelper minioHelper;
        /// <summary>
        /// 
        /// </summary>
        public minioUploadController()
        {
            minioHelper = new MinioHelper();
        }

5.初始化以及上传分片接口

 /// <summary>
 /// 查询上传分片列表
 /// </summary>
 /// <param name="files"></param>
 /// <returns></returns>
 [HttpPost("/file/ListPartsFile")]
 public async Task<dynamic> ListPartsFile(string Name, string UploadId = "")
 {
     List<PartDetail> listPartResponse =new List<PartDetail>();
     try
     {
         listPartResponse = await minioHelper.ListPartsUploadInfo(ConfigConstant.MinioBucketName, "micro-elementtest/" + Name, UploadId);

         Console.WriteLine("查询上传分片列表" + JsonConvert.SerializeObject(listPartResponse));

     }
     catch (MinioException e)
     {
         Log.Error("查询上传分片列表错误: {0}", e.Message);
     }

     return Ok(new {  UploadId, listPartResponse });
 }
 /// <summary>
 /// 合并分片
 /// </summary>
 /// <param name="files"></param>
 /// <returns></returns>
 [HttpPost("/file/ChunkMultipartUpload")]
 public async Task<dynamic> ChunkUploadFile( string Name, string UploadId = "", List<PartETag> partETagList=null )
 {
     CompleteMultipartUploadResponse compResponse=new CompleteMultipartUploadResponse();
     try
     {
         // Complete the multipart upload  分片上传完后合并
         compResponse = await minioHelper.CompleteMultipartUploadFile(ConfigConstant.MinioBucketName, "micro-elementtest/" + Name, UploadId, partETagList);
         Console.WriteLine("分片上传"+ JsonConvert.SerializeObject(compResponse.Key));
     }
     catch (MinioException e)
     {
         Log.Error("文件上传错误: {0}", e.Message);
     }

     return Ok(new {  compResponse });
 }
 /// <summary>
 /// 上传分片
 /// </summary>
 /// <param name="files"></param>
 /// <returns></returns>
 [HttpPost("/file/ChunkUploadFile")]
 public async Task<dynamic> ChunkUploadFile(IFormFile files, [FromForm] string ID, [FromForm]  string Name, [FromForm] long Size, [FromForm] long partSize,  [FromForm] int ChunkCount, [FromForm] string UploadId = "")
 {
     List<PartETag> partETagList = new List<PartETag>();
     try
     {
         // Define input stream
         Stream inputStream = files.OpenReadStream(); //Create13MBDataStream();
         if (string.IsNullOrWhiteSpace(UploadId))
         {
             //初始化分片上传,得到UploadId
             UploadId = await minioHelper.InitMultipartChunkUploadFile(ConfigConstant.MinioBucketName, "micro-elementtest/" + Name);
         }
         PartETag partETag = await minioHelper.ChunkUploadAsync(ConfigConstant.MinioBucketName, "micro-elementtest/" + Name, UploadId, ChunkCount, partSize, inputStream);
         partETagList.Add(partETag);
     }
     catch (MinioException e)
     {
         Log.Error("文件上传错误: {0}", e.Message);
     }

     return Ok(new { UploadId, partETagList, Name });
 }

6.前端页面,采用的vue2+element-ui的框架

<template>
  <div>
    <el-upload class="upload-demo" drag action="" multiple :http-request="handHttpRequest">
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
      <div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
    </el-upload>
  </div>
</template>

<script>
import { ChunkUploadFileApi } from '@/api/upload'
export default {
  name: 'uploadPage',
  data() {
    return {
      uploadId: '',
      ETag1: '',
      ETag2: '',
    }
  },
  methods: {
    handtap() {},
    async handHttpRequest(params) {
      var chunkSize = 5 * 1024 * 1024 // 1MB一片
      const name = params.file.name
      let UploadId = ''
      const uploadNextChunk = async (i) => {
        const file = this.getChunkInfo(params.file, i, chunkSize)
        console.log(file)
        const partSize = file.chunk.size
        const ChunkCount = i+1
        const res = await ChunkUploadFileApi(file.chunk, '123123123123123sa', name, partSize, ChunkCount, 123154545, UploadId)
        console.log(res)
        UploadId = res.uploadId

        if (i < 1) {
          // 仅上传两片,根据需求修改
          await uploadNextChunk(i + 1)
        }
      }

      await uploadNextChunk(0)
    },
    getChunkInfo(file, currentChunk, chunkSize) {
      var start = currentChunk * chunkSize
      var end = Math.min(file.size, start + chunkSize)
      var chunk = file.slice(start, end)
      return {
        start,
        end,
        chunk,
      }
    },
  },
}
</script>

<style></style>

upload文件内容为

import request from '@/utils/request'//这个是request
import base from '@/api/base'//这个是我自己文件的api前缀

const noteApi = {
    ChunkUploadFile: base.outApi + '/file/ChunkUploadFile', //
}

/**
 * 分片测试
 * @returns
 */
export const ChunkUploadFileApi = (file, hash, name, partSize, ChunkCount, size, UploadId) => {
    const formData = new FormData();
    formData.append("files", file);
    formData.append("Name", name);
    formData.append("ID", hash);
    formData.append("PartSize", partSize);
    formData.append("ChunkCount", ChunkCount);
    formData.append("Size", size);
    formData.append("UploadId", UploadId);
    return request({
      url: noteApi.ChunkUploadFile,
      method: "post",
      data: formData,
      headersType: "multipart/form-data",
    });
  };

7.在前端页面上传分片文件

我这里分片是以5MB为一个分片,超过5MB才使用分片的功能
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
通过接口看到上传成功了

8.通过ListPartsFile方法查询已上传的分片

在这里插入图片描述
上传的分片这时候在minio的web控制台是看不到的,需要去minio的存储文件夹里查看路径/data/.minio.sys/multipart
在这里插入图片描述
在这里插入图片描述

9.合并分片

在这里插入图片描述

在这里插入图片描述
合并后在minio桶里可以看到文件了
在这里插入图片描述

10.注意点:

ConfigConstant.MinioBucketName是minio的存储桶名称,这里示例值是micro-element
ConfigConstant.MinioUserName是minio的AccessKey,值以你自己minio服务器的AccessKey为准
ConfigConstant.MinioPassWord是minio的SecretKey,值以你自己minio服务器的SecretKey为准
ConfigConstant.MinioAddressIp是minio的地址,需要带http://ip:port,比如http://192.168.110.11/

1.文件只有大于5MB才能使用分片,如果小于5MB,则不可以使用分片功能

2.文件分片的流程是:先初始化通过InitMultipartChunkUploadFile接口获取uploadID==>通过ChunkUploadAsync接口上传分片的文件(只有最后一片允许小于5MB,前面的分片必须要等于5MB)==>通过CompleteMultipartUploadFile接口合并分片

3.上传分片后还没有合并前,分片文件在桶里不可见,因为分片文件在你的minio存储路径的/minio/data/.minio.sys/multipart路径下

4.合并后文件建议取CompleteMultipartUploadFile接口返回值的key相对路径,如果取绝对路径location的话,以后迁移服务器会使用崩溃~

5.现在已经有了分片的教程,后续的大文件秒传、断点续传可以根据上面的封装接口举一反三,很简单~
(秒传就是判断文件的md5和size大小合并查询数据库是否有重复,如果有,则直接返回文件信息)
(断点续传就是判断文件的md5和size在数据库是否存在并且未上传完成,则根据已上传的文件去判断还有哪些文件没有上传,直接续传即可)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2208363.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高德地图怎么定位自己的店铺?

随着科技的飞速发展&#xff0c;我们的生活也变得更加便利&#xff0c;很多时候只需要一台手机便能解决许多问题&#xff0c;出行方面同样如此。无论你想去哪里&#xff0c;只要在地图导航上输入相应地址&#xff0c;就能随时导航前往目的地。而高德地图作为国内首屈一指的地图…

基于yolov8、yolov5的果蔬检测系统(含UI界面、数据集、训练好的模型、Python代码)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8、yolov8 SE注意力机制 或 yolov5、yolov5 SE注意力机制 &#xff0c; 直接提供最少两个训练好的模型。模型十分重要&#xff0c;因为有些同学的电脑没有 GPU&#xff0…

行测刷题(1)

D D C 论据论点话题不一致优先考虑拆桥&#xff0c;要切断论点和论据之间的联系&#xff0c;一般拆桥的选项都要包含论点和论据不一样的关键词&#xff0c;也就是道德责任和舆论批评 体重变化较小可能也发生了体重减少的状况 主题词:人工培育的水稻 要表示文章对策&#xff0c;…

数学建模算法与应用 第11章 偏最小二乘回归及其方法

目录 11.1 偏最小二乘回归概述 11.2 Matlab 偏最小二乘回归命令 Matlab代码示例&#xff1a;偏最小二乘回归 11.3 案例分析&#xff1a;化学反应中的偏最小二乘回归 Matlab代码示例&#xff1a;光谱数据的PLS回归 习题 11 总结 偏最小二乘回归&#xff08;Partial Least …

Python字符编码详解!

本文简单介绍了各种常用的字符编码的特点&#xff0c;并介绍了在python2.x中如何与编码问题作战 &#xff1a;&#xff09; 请注意本文关于Python的内容仅适用于2.x&#xff0c;3.x中str和unicode有翻天覆地的变化&#xff0c;请查阅其他相关文档。 文章开始前打个小广告——分…

ES创建文档,使用postman调用请求

请求的url 地址是http://192.168.1.108:9200/shopping/_doc&#xff0c;请求方式为post, 请求参数为: { "title":"小米手机", "category":"小米", "images":"http://www.gulixueyuan.com/xm.jpg", "price&…

系统移植一

使用设备是fs4412开发板 一、系统移植 系统移植是将一个操作系统或软件从一个硬件平台或处理器架构转移到另一个平台的过程。系统移植的主要目标是使软件在新的硬件环境下能够正常运行。在系统移植过程中&#xff0c;主要的改动集中在硬件相关的底层部分以及操作系统的核心模…

开源代码编译过程中遇到的问题(持续更新)

一、A-LOAM 地址&#xff1a;GitHub - HKUST-Aerial-Robotics/A-LOAM: Advanced implementation of LOAM 1.error: ‘LocalParameterization’ is not a member of ‘ceres 原因&#xff1a;ceres 版本的问题&#xff0c;A-LOAM 使用的 ceres 版本过低。如果安装的是 ceres 2.2…

大模型微调方法总结(非常详细)零基础入门到精通,收藏这一篇就够了

大模型训练代价很高&#xff0c;国盛证券出过一个报告《ChatGPT需要多少算力》&#xff0c;指出GPT3&#xff08;175B&#xff09;训练一次成本约140万刀&#xff0c;大概是1千万人民币。GPT3已经是2020年的历史了&#xff0c;现在的训练成本可能更高。高昂的训练成本小公司难以…

【ARM汇编速成】零基础入门汇编语言(ARM架构+汇编的实际应用)

目录 一.汇编的前世今生 二.寄存器 三.ARM指令集 1.指令格式 2.寻址方式 3.伪指令 4.基本指令 4.1数据传输指令 4.2存储器访问指令 4.3压栈和出栈指令 4.4跳转指令 4.5算术运算指令 4.6逻辑运算指令 四.C语言与汇编混合编程 1.混合编程前置条件 2.混合编程优势 3.…

活码的3步生成技巧,多种内容快速在线做成二维码

二维码在很多应用场景中都有应用&#xff0c;很多内容可以通过生成活码二维码的方法来提供内容展示。活码可以将多种不同内容给组合展示&#xff0c;而且可以随时在图案不变情况下修改内容&#xff0c;使用起来更加的灵活&#xff0c;常见的活码类型有文本、文件、音视频、图片…

Spring Boot在B2B医疗平台中的病历管理创新

第4章 系统设计 4.1 系统总体设计 系统不仅要求功能完善&#xff0c;而且还要界面友好&#xff0c;因此&#xff0c;对于一个成功的系统设计&#xff0c;功能模块的设计是关键。由于本系统可执行的是一般性质的学习信息管理工作&#xff0c;本系统具有一般适用性&#xff0c;其…

[Git] git stash命令详解

前言 目录 git stash -m git stash list git stash pop git stash apply index git stash drop index git stash clear 特定范围文件储存 git stash [-S|--staged] git stash [-u|--include-untracked] git stash [-a|--all] 将当前未提交的修改(即工作区和暂存区的修…

Go-知识依赖GOPATH

Go-知识依赖GOPATH 1. 介绍2. GOROOT 是什么3. GOPATH 是什么4. 依赖查找5. GOPATH 的缺点 1. 介绍 早期Go语言单纯地使用GOPATH管理依赖&#xff0c;但是GOPATH不方便管理依赖的多个版本&#xff0c;后来增加了vendor&#xff0c;允许把项目依赖 连同项目源码一同管理。Go 1.…

python发邮件附件:配置SMTP服务器与认证?

python发邮件附件技巧&#xff1f;Python发送多附件邮件的方法&#xff1f; Python作为一种强大的编程语言&#xff0c;提供了丰富的库和工具来实现这一功能。AokSend将深入探讨如何使用Python发邮件附件&#xff0c;并详细介绍配置SMTP服务器与认证的关键步骤。 python发邮件…

【CubeMLP】核心方法解读

abstract&#xff1a; 多模态情绪分析和抑郁估计是利用多模态数据预测人类心理状态的两个重要研究课题。以前的研究主要集中在开发有效的融合策略&#xff0c;以交换和整合来自不同模式的心智相关信息。一些基于mlp的技术最近在各种计算机视觉任务中取得了相当大的成功。受此启…

Pandas与JQData在量化投资中的应用:数据获取与处理函数详解

🌟作者简介:热爱数据分析,学习Python、Stata、SPSS等统计语言的小高同学~🍊个人主页:小高要坚强的博客🍓当前专栏:《Python之量化交易》🍎本文内容:Pandas与JQData在量化投资中的应用:数据获取与处理函数详解🌸作者“三要”格言:要坚强、要努力、要学习 目录 …

dvwa:暴力破解、命令注入、csrf全难度详解

暴力破解 easy模式 hydra -L /usr/share/wordlists/SecLists-master/Usernames/top-usernames-shortlist.txt -P /usr/share/wordlists/SecLists-master/Passwords/500-worst-passwords.txt 192.168.72.1 http-get-form "/dvwa/vulnerabilities/brute/:username^USER^&…

UDP和TCP的区别、网络编程(UDP回显服务器、TCP回显服务器)

目录 一、什么是网络编程 二、网络编程的内容概念 接受端和发送端 请求和响应 服务端和客户端 三、UDP和TCP协议的区别 四、UDP网络编程的类和函数&#xff08;回显服务器&#xff09; DatagramSocket DatagramPacket InetSocketAddress 基于UDP的回显服务器和客户…

​​​​​​​​​​Proxifier安装步骤

​​​​​​​​​​Proxifier ​​​​​​​​​​Proxifier下载连接 进入Proxifier官网后&#xff0c;点击下面按钮&#xff0c;下载安装包 下载完成后&#xff0c;打开压缩包 双击运行 选择试用 安装完成