我的编程空间,编程开发者的网络收藏夹
学习永远不晚

minio分片上传

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

minio分片上传

oss文件服务

一、前言

​ Minio是一个对象存储服务OSS(Object Storage Service)。是⼀种海量、安全、低成本、⾼可靠的云存储服务。本身的应用的并不复杂。

但是Minio的APi在对于大于5m的文件,自动采用了分片上传,它的分片上传我们无法得知上传的分片后的序号,也就是说,没上传一个分片,我们都需要自己去记录已上传分片的序号。这将导致一个文件一个文件分片5个,那么同样还需要调用5次后端接口去记录这5个分片的信息。这个无疑大大浪费了性能,且无法做到并发上传。

​ 因此基于Minio的javaAPI,我们采用另一种方案去替代。

二、初步流程:

image-20221107005827298

  1. 前端服务进行大文件分片处理,将分片信息传递给文件服务oss。
  2. oss通过redis和mysql检查分片信息是否已存在,若存在直接返回数据。
  3. 若不存在oss生成上传链接以及uploadID, 然后记录并返回所有分片的上传链接及uploadId。
  4. 前端服务直接请求Minio 服务器,并发上传分片。
  5. 所有分片上传完成后,使用uploadId 调用文件服务进行文件合并,oss同时更新上传状态。

三、具体实现

1 相关配置准备

1.1 数据库表设计

表名:file_record

字段名类型注释
idbigint主键id
file_urltext上传分片的链接
file_namevarchar文件名
md5varcharMD5
upload_idvarchar上传id
is_uploadedint是否已上传
total_chunksint分片总块数
sizebigint文件大小(K)
completed_partsint已完成片数
created_atdatetime生成时间
updated_atdatetime更新时间
deleted_atdatetime删除时间(软删除)

1.2 minio集成配置

linux上部署minio,docker下载minio镜像

docker run -d -p 10000:10000 -p 11000:11000 --name minio2 -v ~/var/local/environment/data:/data -e "MINIO_ROOT_USER=root" -e "MINIO_ROOT_PASSWORD=minio123456" minio/minio server /data --console-address ":11000" --address ":10000"

配置说明:这里选择开放两个端口10000和11000。

–console-address ":11000"是将控制台的端口11000暴露

–address ":10000"是我们集成到项目中占用的端口。

nacos上配置oss-dev.yml

minio:  url: http://192.168.137.129:10000  accessKey: root  secretKey: minio123456  bucketName: test-bucket

minio控制台(可管理文件和bucket):

image-20221106231456674

项目依赖引入,这里使用的是当前的最新版8.3.5

<dependency>    <groupId>io.miniogroupId>    <artifactId>minioartifactId>    <version>8.3.5version>dependency>

2 后端实现

2.1 MinioProperty

MinioProperty类获取minio配置

import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Configuration;@Data@Configuration@ConfigurationProperties(prefix = "minio")public class MinioProperty {        public String url;        public String accessKey;        public String secretKey;        public String bucketName;        public Boolean secure = false;}

2.2 MyMinioClient

MyMinioClient类继承MinioClient来暴露出父类方法供扩展使用。

import com.google.common.collect.Multimap;import io.minio.CreateMultipartUploadResponse;import io.minio.ListPartsResponse;import io.minio.MinioClient;import io.minio.ObjectWriteResponse;import io.minio.errors.*;import io.minio.messages.Part;import java.io.IOException;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;public class MyMinioClient extends MinioClient {    protected MyMinioClient(MinioClient client) {        super(client);    }        @Override    public CreateMultipartUploadResponse createMultipartUpload(String bucketName, String region, String objectName, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {        return super.createMultipartUpload(bucketName, region, objectName, headers, extraQueryParams);    }        @Override    public ObjectWriteResponse completeMultipartUpload(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {        return super.completeMultipartUpload(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams);    }        public ListPartsResponse listMultipart(String bucketName, String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {        return super.listParts(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);    }        public CreateMultipartUploadResponse uploadId(String bucketName, String region, String objectName, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {        return super.createMultipartUpload(bucketName, region, objectName, headers, extraQueryParams);    }        @SneakyThrows    public String getPresignedObjectUrl(String bucketName, String filePath, Map<String, String> queryParams) {        return super.getPresignedObjectUrl(                GetPresignedObjectUrlArgs.builder()                        .method(Method.PUT)                        .bucket(bucketName)                        .object(filePath)                        .expiry(1, TimeUnit.DAYS)                        .extraQueryParams(queryParams)                        .build());    }}

2.3 MinioConfig

MinioConfig将MyMinioClient交给spring容器管理方便调用

import io.minio.MinioClient;import lombok.SneakyThrows;import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class MinoConfig {    @Bean    @SneakyThrows    @ConditionalOnMissingBean(MyMinioClient.class)    public MyMinioClientminioClient(MinioProperty minioProperty) {        MinioClient minioClient = MinioClient.                builder()                .endpoint(minioProperty.getUrl())                .credentials(minioProperty.getAccessKey(), minioProperty.getSecretKey())                .build();        return new MyMinioClient(minioClient);    }}

2.4 核心接口实现

2.4.1 createMultipartUpload方法

返回分片上传需要的签名数据URL及 uploadId。

fileMultipartDTO 是分片传输实体,包括:文件名fileName、分片数chunkSize、所属文件夹名bucketName。

检查redis中是否存在,若存在直接返回对应数据;若redis中不存在检查数据库中是否存在,若存在直接返回对应数据。

若redis、数据库中都不存在,处理生成分片。

调用minioAPI获得uploadId和签名url

相关数据在redis中缓存,并保存到数据库。返回给前端

public Map<String, Object> createMultipartUpload(FileMultipartDTO fileMultipartDTO) {    log.info("fileMultipartDTO:{}", fileMultipartDTO);    String fileName = fileMultipartDTO.getFileName();    Integer chunkSize = fileMultipartDTO.getChunkSize();    String bucketName = fileMultipartDTO.getBucketName();    // 1. 根据文件名创建签名    // 2. 获取uploadId    String contentType = "application/octet-stream";    HashMultimap<String, String> headers = HashMultimap.create();    headers.put("Content-Type", contentType);    CreateMultipartUploadResponse response = minioClient.uploadId(bucketName, null, fileName, null, null);    String uploadId = response.result().uploadId();    Map<String, Object> result = new HashMap<>(3, 1);    String md5 = MD5Utils.encryptToMd5(fileName + chunkSize + bucketName);    //查询是否已存在,若存在返回对应数据    Map<String, Object> checkResult;    //检查redis中是否已存在    checkResult = checkIsExistInRedis(md5);    if (checkResult != null && !checkResult.isEmpty()) {        log.info(">>>>>>>>>>>>redis中存在");        return checkResult;    }    //检查数据库中是否已存在    checkResult = checkIsExistInDatabase(md5);    if (checkResult != null && !checkResult.isEmpty()) {        log.info(">>>>>>>>>>>>数据库中存在");        //redis中补充        checkResult.put("fileName", fileName);        redisTemplate.opsForValue().set(RedisPrefixForKey.MINIO_KEY + md5, checkResult, 1, TimeUnit.HOURS);        return checkResult;    }    //redis中和数据库中都没有,需要生成    result.put("uploadId", uploadId);    // 3. 请求Minio 服务,获取每个分块带签名的上传URL    Map<String, String> reqParams = new HashMap<>(3, 1);    reqParams.put("uploadId", uploadId);    List<String> uploadUrlList = new ArrayList<>();    // 4. 循环分块数 从1开始    for (int i = 1; i <= chunkSize; i++) {        reqParams.put("partNumber", String.valueOf(i));        // 获取URL        String uploadUrl = minioCilent.getPresignedObjectUrl(bucketName, fileName, reqParams);        // 添加到集合        result.put("chunk_" + (i - 1), uploadUrl);        uploadUrlList.add(uploadUrl);    }    log.info(">>>>分片数据入redis和数据库");    result.put("fileName", fileName);    redisTemplate.opsForValue().set(RedisPrefixForKey.MINIO_KEY + md5, result, 8, TimeUnit.HOURS);    //入库    String uploadUrls = JSON.toJSONString(uploadUrlList);    FileRecord fileRecord = new FileRecord()        .setFileName(fileName)        .setFileUrl(uploadUrls)        .setUploadId(uploadId)        .setTotalChunks(chunkSize)        .setMd5(md5)        //上传完成后,接收到合并请求时再改以下参数        .setCompletedParts(0)        .setSize(0)        .setIsUploaded(0);    fileRecordMapper.insert(fileRecord);    return result;}
2.4.2 completeMultipartUpload方法

分片上传完后合并。

FileCompleteDTO是合并请求参数实体,包括:所属文件夹名bucketName、上传iduploadId、文件名objectName。

调用minioAPI获取该文件的所有分片partList。

将partList中part合并成parts。

调用minioAPI完成合并。

合并成功后修改数据库信息。

public Result completeMultipartUpload(FileCompleteDTO fileObject) {    String bucketName = fileObject.getBucketName();    String uploadId = fileObject.getUploadId();    String objectName = fileObject.getObjectName();    int completedParts;    int size = 0;    try {        Part[] parts = new Part[10000];        ListPartsResponse partResult = minioClient.listMultipart(bucketName, null, objectName, 1000, 0, uploadId, null, null);        List<Part> partList = partResult.result().partList();        completedParts = partList.size();        int partNumber = 1;        log.info("总片数:================" + completedParts + "========");        for (Part part : partList) {            parts[partNumber - 1] = new Part(partNumber, part.etag());   //etag就是分片信息            partNumber++;            size += part.partSize();        }        minioClient.completeMultipartUpload(bucketName, null, objectName, uploadId, parts, null, null);    } catch (Exception e) {        e.printStackTrace();        return Result.fail("合并失败:" + e.getMessage());    }    //合并成功,入库    //通过uploadId修改对应的记录行    fileRecordMapper.updateStatusByUploadId(uploadId, completedParts, size);    return Result.success();}

3 前端实现

此处对前端实现不做重点描述,项目提供了一个demo,后续可做参考修改。
在这里插入图片描述

四、效果展示

当前端选择完文件后:

image-20221106234039847

image-20221106234221457

数据库中新增数据:

image-20221106234346147

redis中新增数据

image-20221106234426565

当前端点击开始上传按钮后,前端服务直接请求Minio服务

image-20221106234606478

完成上传后,数据库更新is_uploaded、Size、completed_parts

image-20221106234715043

查看minio控制台,可以看到该文件。

image-20221106234840310

来源地址:https://blog.csdn.net/qq_42852943/article/details/127734905

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

minio分片上传

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

minio分片上传

oss文件服务 一、前言 ​ Minio是一个对象存储服务OSS(Object Storage Service)。是⼀种海量、安全、低成本、⾼可靠的云存储服务。本身的应用的并不复杂。 但是Minio的APi在对于大于5m的文件,自动采用了分
2023-08-19

将图像上传到 MinIO

小伙伴们对Golang编程感兴趣吗?是否正在学习相关知识点?如果是,那么本文《将图像上传到 MinIO》,就很适合你,本篇文章讲解的知识点主要包括。在之后的文章中也会多多分享相关知识点,希望对大家的知识积累有所帮助!问题内容我想将.png格
将图像上传到 MinIO
2024-04-05

minio分布式存储上传很慢怎么解决

minio分布式存储上传慢可能有多种原因,可以尝试以下几种解决方案:网络问题:确保网络连接稳定,上传过程中没有网络波动或者丢包问题。可以尝试使用不同的网络环境上传文件,看是否有改善。节点负载:检查minio各节点的负载情况,确保节点之间的负
minio分布式存储上传很慢怎么解决
2024-04-09

图片上传——PHP图片上传

图片上传在项目中经常用到,几乎没有任何一个项目可以脱离图片或者是文件上传。本篇我在这向大家介绍两种常规的上传方式。(注:在这里我们仅仅是对功能的实现,不去做过多的前端的样式)一、利用form表单上传此种方式是最原始的上传方式,前端就是简单的
图片上传——PHP图片上传
2024-02-27

Python怎么实现上传Minio文件

本篇内容介绍了“Python怎么实现上传Minio文件”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!环境依赖安装minio以及oss2依赖p
2023-06-25

android文件上传示例分享(android图片上传)

主要思路是调用系统文件管理器或者其他媒体采集资源来获取要上传的文件,然后将文件的上传进度实时展示到进度条中。 主Activity代码如下:package com.guotop.elearn.activity.app.yunpan.activ
2022-06-06

Java实现文件分片上传

起因:最近在工作中接到了一个大文件上传下载的需求,要求将文件上传到share盘中,下载的时候根据前端传的不同条件对单个或多个文件进行打包并设置目录下载。 一开始我想着就还是用老办法直接file.transferTo(newFile)就算是大
2023-08-18

如何使用大文件上传:秒传、断点续传、分片上传方法

本篇内容介绍了“如何使用大文件上传:秒传、断点续传、分片上传方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!秒传1、什么是秒传通俗的说,你
2023-06-15

TypeScript前端上传文件到MinIO示例详解

这篇文章主要为大家介绍了TypeScript前端上传文件到MinIO示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Django上传图片

from django.db import modelsclass Picture(models.Model): path = models.ImageField(upload_to='share_pictures/') nam
2023-01-31

PHP 上传图片

我们可以使用简单的文件上传操作在 PHP 中上传图像,但首先,应该从 php.ini 文件启用文件上传。本教程演示如何在 PHP 中上传图像。在 PHP 中启用从 php.ini 文件上传文件以上传图像对于较新版本的 PHP,文件上传默认为
PHP 上传图片
2024-02-27

Java怎么将大文件分片上传

这篇文章主要介绍“Java怎么将大文件分片上传”,在日常操作中,相信很多人在Java怎么将大文件分片上传问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java怎么将大文件分片上传”的疑惑有所帮助!接下来,请跟
2023-07-02

Java的分片上传功能的实现

本文主要介绍了Java的分片上传功能的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-02-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录