spring boot 阿里云oss 文件分片上传、断点续传

这篇具有很好参考价值的文章主要介绍了spring boot 阿里云oss 文件分片上传、断点续传。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文章目录

  • 前言
  • 一、申请阿里云oss
  • 二、上代码
  • 总结

前言

      阿里云对象存储OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,可提供99.9999999999%(12个9)的数据持久性,99.995%的数据可用性。多种存储类型供选择,全面优化存储成本。

    您可以使用阿里云提供的API、SDK接口或者OSS迁移工具轻松地将海量数据移入或移出阿里云OSS。数据存储到阿里云OSS以后,您可以选择标准存储(Standard)作为移动应用、大型网站、图片分享或热点音视频的主要存储方式,也可以选择成本更低、存储期限更长的低频访问存储(Infrequent Access)、归档存储(Archive)、冷归档存储(Cold Archive)或者深度冷归档(Deep Cold Archive)作为不经常访问数据的存储方式。

一、申请阿里云oss

阿里云-计算,为了无法计算的价值

选择对象存储oss

oss分片上传,python,数据挖掘,机器学习

 选择立即开通oss分片上传,python,数据挖掘,机器学习

 创建bucket(桶)

oss分片上传,python,数据挖掘,机器学习

创建Bucket 名称

选择就近地域

其他的默认就可

oss分片上传,python,数据挖掘,机器学习

 获取AccessKeyID 和AccessKeySecret

 oss分片上传,python,数据挖掘,机器学习

oss分片上传,python,数据挖掘,机器学习

 oss分片上传,python,数据挖掘,机器学习

二、使用步骤

1.引入oss依赖

        <!-- oss -->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.8.0</version>
        </dependency>

2.阿里云配置

  在resourse下新建 aliyunOSS.properties文件文章来源地址https://www.toymoban.com/news/detail-732138.html

#之前获取的AccessKeyID
aliyun.AccessKeyID=
#之前获取的AccessKeySecret
aliyun.AccessKeySecret=
#你创建桶的名称
aliyun.Buckets=
#桶的外网地址 在桶的概览下的外网地址 例:oss-cn-hangzhou.aliyuncs.com 
aliyun.EndPoint=
#自定义前缀
aliyun.prefix=
#限制单个上传的最大的MB
aliyun.MAX_SIZE =

3.oss工具类


/**
 * @Description
 * @Author LuoAC
 * @Date 2023/6/19 16:38
 */
public class test {
    /**
     * 阿里云的配置参数
     */
    private static String accessKeyId = null;
    private static String accessKeySecret = null;
    private static String endpoint = null;
    private static String bucketName = null;
    private static Integer MAX_SIZE = null;
    /**
     * 存储在OSS中的前缀名
     */
    private static String file_prefix = null;


    private static volatile OSSClient ossClient = null;

    private static volatile RedisCache redisManager = null;


    static {
        //初始化AccessKey
        accessKeyId = PropertiesReader.get("aliyun.AccessKeyID");
        //初始化AccessKeySecret
        accessKeySecret = PropertiesReader.get("aliyun.AccessKeySecret");
        //初始化Endpoint
        endpoint = PropertiesReader.get("aliyun.EndPoint");
        //初始化bucketName
        bucketName = PropertiesReader.get("aliyun.Buckets");
        //初始化前缀
        file_prefix = PropertiesReader.get("aliyun.prefix");
        //文件最大值
        MAX_SIZE = PropertiesReader.getInteger("aliyun.MAX_SIZE");
    }

    /**
     * @return skipUpload 判断文件是否存在的标识。true即上传完成。再次上传可秒传
     * @return key  文件key
     * @return bucket 桶名称
     * @return uploadId 上传id
     * @return uploaded 已经上传分片的list<Integer>
     * @Description 首次分片 信息
     * @Author LuoAC
     * @Date 2023/6/9 9:55
     */
    public static Map ossCheck(UploadChunkFileParam param) {
        RedisCache redisManager = initRedisManager();
        //文件md5
        String identifier = param.getIdentifier();
        //文件后缀
        String suffix = param.getSuffix();
        //文件的key
        String key = identifier + "." + suffix;
        Map<String, Object> map = MapUtil.newHashMap();
        // 判断是否上传过  秒传
        if (checkExist(key)) {
            map.put("skipUpload", true);
            map.put("key", key);
            map.put("bucket", bucketName);
            return map;
        }
        // 判断是否第一次上传   是否做断点续传
        String uploadId = redisManager.getCacheObject(key);
        //第一次上传
        if (StringUtils.isEmpty(uploadId)) {
            String uploadIdNew = uploadChunkInit(key);
            map.put("skipUpload", false);
            map.put("uploadId", uploadIdNew);
            map.put("uploaded", null);
            redisManager.setCacheObject(key, uploadIdNew);
            return map;
        } else {
            // 继续上传
            Map<String, String> uploadedCache = redisManager.hmget(CacheConstants.REDIS_ALI_OSS_KEY + uploadId);
            List<Integer> uploaded = Lists.newArrayList();
            for (Map.Entry<String, String> entry : uploadedCache.entrySet()) {
                uploaded.add(JSONUtil.toBean(entry.getValue(), PartETag.class).getPartNumber());
            }
            map.put("skipUpload", false);
            map.put("uploadId", uploadId);
            map.put("uploaded", uploaded);
            return map;
        }
    }


    /**
     * 分片上传
     *
     * @param param 上传参数
     * @return
     */
    public static Map uploadChunk(UploadChunkFileParam param) {
        return uploadChunk(param.getUploadId(), param.getIdentifier(), param.getSuffix(), param.getFile(), param.getChunkNumber(),
                param.getCurrentChunkSize(), param.getTotalChunks(), param.getFilename(),param.getProjectId(),param.getDirId());
    }
    /**
     * 分片上传
     * 1、检查文件是否上传
     * 2、检查文件是否第一次上传,第一次上传创建上传id uploadId
     * 3、检查是否是断点续传,如果是返回已上传的分片
     * 4、分片上传到阿里云OSS上,并记录上传信息到Redis
     * 5、判断是否已上传完成,已完成:合并所有分片为源文件
     *
     * @param uploadId   上传id
     * @param identifier        文件在OSS上的key
     * @param file       文件分片
     * @param chunkIndex 分片索引
     * @param chunkSize  分片大小
     * @param chunkCount 总分片数w
     * @return
     */
    public static Map uploadChunk(String uploadId, String identifier, String suffix, MultipartFile file, Integer chunkIndex,
                                  long chunkSize, Integer chunkCount, String fileName, Integer projectId, String dirId) {
        ossClient = initOSS();
        String key=identifier+"."+suffix;
        try {
            Map<String, Object> map = MapUtil.newHashMap();
            RedisCache redisManager = initRedisManager();

            // 上传分片
            PartETag partETag = uploadChunkPart(uploadId, key, file.getInputStream(), chunkIndex, chunkSize, chunkCount);
            // 分片上传完成缓存key
            redisManager.hset(CacheConstants.REDIS_ALI_OSS_KEY + uploadId, chunkIndex + ",", JSONUtil.toJsonStr(partETag));
            // 取出所有已上传的分片信息
            Map<String, String> dataMap = redisManager.hmget(CacheConstants.REDIS_ALI_OSS_KEY + uploadId);
            List<PartETag> partETagList = Lists.newArrayList();

            //已经上传的片数
            Integer i = 0;
            for (Map.Entry<String, String> entry : dataMap.entrySet()) {
                partETagList.add(JSONUtil.toBean(entry.getValue(), PartETag.class));
                i++;
            }
            List<Integer> list = partETagList.stream().map(PartETag::getPartNumber).collect(Collectors.toList());
            // 已上传的百分比
            String percent = String.format("%.2f", (double) i / chunkCount);
            // 分片上缓存的待传百分比
            UploadChunkFileParam uploadChunkFileParam = new UploadChunkFileParam();

            uploadChunkFileParam.setPercent(percent);
            uploadChunkFileParam.setFilename(fileName);
            uploadChunkFileParam.setProjectId(projectId);
            uploadChunkFileParam.setDirId(dirId);
            uploadChunkFileParam.setUploaded(list);
            uploadChunkFileParam.setIdentifier(identifier);
            uploadChunkFileParam.setSuffix(suffix);
            uploadChunkFileParam.setUploadId(uploadId);
            redisManager.setCacheMapValue(CacheConstants.REDIS_ALI_OSS_KEY + SecurityUtils.getUserId(),uploadId+","+key,uploadChunkFileParam);
            // 判断是否上传完成
            if (dataMap.keySet().size() == chunkCount) {
                uploadChunkComplete(uploadId, key, partETagList);
                for (String mapKey : dataMap.keySet()) {
                    redisManager.hdel(CacheConstants.REDIS_ALI_OSS_KEY + uploadId, mapKey);
                }
                redisManager.deleteObject(key);
                redisManager.hdel(CacheConstants.REDIS_ALI_OSS_KEY+SecurityUtils.getUserId(),uploadId+","+key);
                map.put("skipUpload", true);
                map.put("uploadId", uploadId);
                map.put("key", key);
                map.put("bucket", bucketName);
                map.put("fileName",fileName);
                ossClient.setObjectAcl(bucketName, key, CannedAccessControlList.PublicRead);
            } else {
                map.put("uploaded", list);
                map.put("skipUpload", false);
                map.put("uploadId", uploadId);
            }
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("上传失败:" + e.getMessage());
        }
    }

    /**
     * 上传分片文件
     *
     * @param uploadId   上传id
     * @param key        key
     * @param instream   文件分片流
     * @param chunkIndex 分片索引
     * @param chunkSize  分片大小
     * @return
     */
    public static PartETag uploadChunkPart(String uploadId, String key, InputStream instream,
                                           Integer chunkIndex, long chunkSize, Integer chunkCount) {
        ossClient = initOSS();
        try {
            UploadPartRequest partRequest = new UploadPartRequest();
            // 阿里云 oss 文件根目录
            partRequest.setBucketName(bucketName);
            // 文件key
            partRequest.setKey(key);
            // 分片上传uploadId
            partRequest.setUploadId(uploadId);
            // 分片文件
            partRequest.setInputStream(instream);
            // 分片大小。除了最后一个分片没有大小限制,其他的分片最小为100 KB。
            partRequest.setPartSize(chunkSize);
            System.out.println(chunkSize + "    " + chunkIndex + "   " + uploadId);
            // 分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出这个范围,OSS将返回InvalidArgument的错误码。
            partRequest.setPartNumber(chunkIndex);
            // 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
            UploadPartResult uploadPartResult = ossClient.uploadPart(partRequest);
            // 每次上传分片之后,OSS的返回结果包含PartETag。PartETag将被保存在redis中。
            return uploadPartResult.getPartETag();
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("分片上传失败:" + e.getMessage());
        }
    }

    /**
     * 文件合并
     *
     * @param uploadId  上传id
     * @param key       key
     * @param chunkTags 分片上传信息
     * @return
     */
    public static CompleteMultipartUploadResult uploadChunkComplete(String uploadId, String key, List<PartETag> chunkTags) {
        ossClient = initOSS();
        try {
            CompleteMultipartUploadRequest completeMultipartUploadRequest =
                    new CompleteMultipartUploadRequest(bucketName, key, uploadId, chunkTags);
            CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(completeMultipartUploadRequest);

            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("分片合并失败:" + e.getMessage());
        }
    }
    /**
     * @Description 初始化OSSClient
     * @Author LuoAC
     * @Date 2023/6/8 10:53
     */
    private static OSSClient initOSS() {
        if (ossClient == null) {
            synchronized (OSSClient.class) {
                if (ossClient == null) {
                    ossClient = new OSSClient(endpoint, new DefaultCredentialProvider(accessKeyId, accessKeySecret),
                            new ClientConfiguration());
                }
            }
        }
        return ossClient;
    }

    /**
     * 初始化上传id uploadId
     *
     * @param key
     * @return
     */
    public static String uploadChunkInit(String key) {
        if (StringUtils.isEmpty(key)) {
            throw new CustomException("key不能为空");
        }
        ossClient = initOSS();
        try {
            // 创建分片上传对象
            InitiateMultipartUploadRequest uploadRequest = new InitiateMultipartUploadRequest(bucketName, key);
            // 初始化分片
            InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(uploadRequest);
            // 返回uploadId,它是分片上传事件的唯一标识,您可以根据这个uploadId发起相关的操作,如取消分片上传、查询分片上传等。
            return result.getUploadId();
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("初始化分片失败:" + e.getMessage());
        }
    }

    /**
     * @Description 判断桶中是否存在这个key
     * @Author LuoAC
     * @Date 2023/6/8 14:00
     */
    public static Boolean checkExist(String key) {
        ossClient = initOSS();
        return ossClient.doesObjectExist(bucketName, key);
    }

    /**
     * @Description 获取redis
     * @Author LuoAC
     * @Date 2023/6/8 14:11
     */
    private static RedisCache initRedisManager() {
        if (redisManager == null) {
            synchronized (RedisCache.class) {
                if (redisManager == null) {
                    return SpringUtils.getBean(RedisCache.class);
                }
            }
        }
        return redisManager;
    }
}

package com.hzzd.web.services.domain;


import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

/**
 * @Description
 * @Author LuoAC
 * @Date 2023/6/7 17:18
 */

@Data
public class UploadChunkFileParam {


    /**
     * 文件传输任务ID
     * 文件MD5编码
     */
    private String identifier;

    /**
     * 文件全名称 例如:123.png
     */
    private String filename;


     /** 后缀*/
    private String suffix;

    /**
     * 主体类型--这个字段是我项目中的其他业务逻辑可以忽略
     */
    private String objectType;
    /**
     * 分片总数
     */
    private int totalChunks;

    /**
     * 每个分块的大小
     */
    private long chunkSize;
    /**
     * 当前为第几分片
     */
    private int chunkNumber;
    /**
     * 当前分片大小
     */
    private long currentChunkSize;

    /**
     * 分块文件传输对象
     */
    private MultipartFile file;

    /**
     * oss上传时的上传id
     */
    private String uploadId;

    /**
     * oss上传时的文件key
     */
    private String key;

    /**
     * oss上传时的文件key
     */
    private String percent;


    @ApiModelProperty("工程id")
    private Integer projectId;

    @ApiModelProperty("目录id")
    private String dirId;

    /**
     * 未上传的id
     */
    private List<Integer> uploaded;

}

oss 分片上传笔记

到了这里,关于spring boot 阿里云oss 文件分片上传、断点续传的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端文件上传(文件上传,分片上传,断点续传)

    普通文件上传 思路: 首先获取用户选择的文件对象,并将其添加到一个 FormData 对象中。然后,使用 axios 的 post 方法将 FormData 对象发送到服务器。在 then 和 catch 中,我们分别处理上传成功和失败的情况,并输出相应的信息。 需要注意,在使用 axios 进行文件上传时,必须将

    2024年02月22日
    浏览(43)
  • aliyun-oss-sdk阿里云OSS视频上传(断点续传)前端实现

    最近上传视频的功能,突然炸了,两年没动的代码,突然不行辽,首次上传成功,后面继续上传就可以,但凡有一次上传失败,再上传文件就不行。 这里博主使用的是凭证上传方式哈。 官方文档:Web(JavaScript)上传SDK 查看了官方文档,确实是提到了,如使用1.5.3之前版本重

    2024年02月07日
    浏览(40)
  • Minio大文件分片上传、断点续传实现

    使用minio api实现分片上传及断点续传功能。 前端准备:获取大文件的MD5值,将文件分片,5M为一分片,排好顺序,并按顺序命名(1,2,3这种后面比较好合并) 在上传分片阶段,前端有上传进度条 1、检验文件MD5值 1.1 redis中查看MD5是否存在 1.2 判断临时文件夹是否存在 boolean d

    2024年02月09日
    浏览(53)
  • JAVA面试题分享五百一十一:Spring Boot基于WebUploader实现超大文件上传和断点续传

    目录 前言 目标 实现思路 大文件分片 合并分片 断点续传 代码实现 1、webuploader组件中,分片上传怎么开启? 2、webuploader组件中,文件的md5值如何计算? 3、webuploader组件中,分片文件的md5值如何计算? 4、webuploader组件中,分片上传的的请求在哪里触发? 5、前端、后端如何校

    2024年02月19日
    浏览(61)
  • minio&前后端分离上传视频/上传大文件——前后端分离断点续传&minio分片上传实现

    🍀🍀🍀🍀分布式文件系统-minio: 第一章:分布式文件系统介绍与minio介绍与使用(附minio java client 使用) 第二章:minio前后端分离上传视频/上传大文件——前后端分离断点续传minio分片上传实现 断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包

    2024年02月03日
    浏览(55)
  • 【SpringBoot整合系列】SpringBoot 实现大文件分片上传、断点续传及秒传

    小文件(图片、文档、视频)上传可以直接使用很多ui框架封装的上传组件,或者自己写一个input 上传,利用FormData 对象提交文件数据,后端使用spring提供的MultipartFile进行文件的接收,然后写入即可。 但是对于比较大的文件,比如上传2G左右的文件(http上传),就需要将文件

    2024年04月16日
    浏览(67)
  • springboot整合vue2-uploader文件分片上传、秒传、断点续传

    vue-simple-uploader 是基于 simple-uploader.js 封装的vue上传插件。它的优点包括且不限于以下几种: 支持文件、多文件、文件夹上传;支持拖拽文件、文件夹上传 可暂停、继续上传 错误处理 支持“秒传”,通过文件判断服务端是否已存在从而实现“秒传” 分片上传 支持进度、预估

    2024年02月06日
    浏览(61)
  • 基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传

    1. 前言 文件上传 小文件(图片、文档、视频)上传可以直接使用很多ui框架封装的上传组件,或者自己写一个input 上传,利用FormData 对象提交文件数据,后端使用spring提供的MultipartFile进行文件的接收,然后写入即可。但是对于比较大的文件,比如上传2G左右的文件(http上传

    2024年02月06日
    浏览(48)
  • 【万字长文】Vue+SpringBoot实现大文件秒传、断点续传和分片上传完整教程(提供Gitee源码)

    前言:最近在实际项目中碰到一个需求,客户可能会上传比较大的文件,如果采用传统的文件上传方案可能会存在服务器压力大、资源浪费甚至内存溢出的一些安全风险,所以为了解决一系列问题,需要采用新的技术方案来实现大文件的上传;空闲的时候参考了网上的一些相

    2024年02月12日
    浏览(47)
  • springboot 、html分片上传,断点续传

    后端代码 注意:合并分片代码中: Files.write(mergedFilePath, Files.readAllBytes(chunkFilePath), StandardOpenOption.APPEND); 如果不设置该值 StandardOpenOption.APPEND ,无法打开合并后的文件 前端代码

    2024年02月05日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包