SpringBoot整合阿里云OSS上传文件,多线程分片上传,普通上传,文件删除

阿里云OSS多线程分片上传

1、首先开通阿里云OSS存储,这里不多说了

2、创建一个Bucket

在这里插入图片描述
在这里插入图片描述
按照你自己的需求选择

3、创建好之后,点击Access Key,来获取accessKeyId、accessKeySecret这两个参数

在这里插入图片描述

4. Maven依赖

 		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
         <!--aliyun-oss-->
		<dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.8.0</version>
        </dependency>
        <!--commons-fileupload-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
        <!--lombok-->
         <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

5、YML

#阿里云 OSS
aliyun:
  oss:
   file:
     endpoint: *********  #Endpoint(地域节点)
     accessKeyId: **********           #你自己的accessKeyId
     accessKeySecret: *********  #你自己的AccessKeySecret
     bucketName: *****           #Bucket存储空间
     url: *******    #Bucket 域名

6、新建一个AliyunOSS.java 实体类

@Data
@Component
@ConfigurationProperties("aliyun.oss.file")
public class AliyunOSS {

    private String endpoint;  //Endpoint(地域节点)
    private String accessKeyId;  //accessKeyId
    private String accessKeySecret; //AccessKeySecret
    private String bucketName;    //Bucket存储空间
    private String url;  //Bucket 域名
    private String[] fileHost;
}

7.新建一个AliyunOSSServiceImpl.java

package cc.flysoft.m.service.impl;

import cc.flysoft.m.entity.AliyunOSS;
import cc.flysoft.m.service.AliyunOSSService;
import cc.flysoft.m.util.FileName;
import com.aliyun.oss.*;
import com.aliyun.oss.model.*;
import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;


/**
 * @author zhanglin
 * @descibe AliyunOSS
 * @date 2022/6/03 15:06
 */
@Service
public class AliyunOSSServiceImpl implements AliyunOSSService {
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(AliyunOSSServiceImpl.class);
    @Autowired
    private AliyunOSS aliyunOSS;

    /**
     * 普通文件上传
     */
    @Override
    public String upLoad(MultipartFile file) {
        logger.info("\n<<------OSS文件上传开始-------->>\n"+file.getOriginalFilename());
        // 判断文件
        if (file == null) {
            return null;
        }
        OSSClient client = new OSSClient(aliyunOSS.getEndpoint(), aliyunOSS.getAccessKeyId(),aliyunOSS.getAccessKeySecret());
        try {
            // 判断容器是否存在,不存在就创建
            if (!client.doesBucketExist(aliyunOSS.getBucketName())) {
                client.createBucket(aliyunOSS.getBucketName());
                CreateBucketRequest createBucketRequest = new CreateBucketRequest(aliyunOSS.getBucketName());
                createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
                client.createBucket(createBucketRequest);
            }
            //文件重命名
            String fileUrl = this.getFileName(file);
            // 上传文件
            PutObjectResult result = client.putObject(new PutObjectRequest(aliyunOSS.getBucketName(), fileUrl, file.getInputStream()));
            // 设置权限(公开读)
            client.setBucketAcl(aliyunOSS.getBucketName(), CannedAccessControlList.PublicRead);
            if (result != null) {
                logger.info("\n<<------OSS文件上传成功------>>\n "+aliyunOSS.getUrl() + fileUrl);
                return aliyunOSS.getUrl()+ fileUrl;
            }
        } catch (OSSException oe) {
            logger.error(oe.getMessage());
        } catch (ClientException ce) {
            logger.error(ce.getErrorMessage());
        } catch (IOException e) {
            e.printStackTrace();
            logger.error(e.getMessage());
        } finally {
            if (client != null) {
                client.shutdown();
            }
        }
        return  null;
    }

    /**
     * 多线程,大文件分片上传
     * @param file
     * @return
     */
    @Override
    public String fragmentUpload(MultipartFile file){
        long startTime = System.currentTimeMillis();
        //构造一个线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(12,
                20,
                60,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(200));
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(aliyunOSS.getEndpoint(), aliyunOSS.getAccessKeyId(), aliyunOSS.getAccessKeySecret());
        //设置为公共读,私有写
        ossClient.setBucketAcl(aliyunOSS.getBucketName(), CannedAccessControlList.PublicRead);
        //重命名后文件名
        String objectName = getFileName(file);
        try {

            // 创建OSSClient实例。
            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(aliyunOSS.getBucketName(), objectName);
            // 初始化分片。
            InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
            // 返回uploadId,它是分片上传事件的唯一标识,您可以根据这个uploadId发起相关的操作,如取消分片上传、查询分片上传等。
            String uploadId = upresult.getUploadId();
            // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
            List<PartETag> partETags = new ArrayList<PartETag>();
            // 设置每块为 1M
            final long partSize = 1 * 1024 * 1024L;
            // 转换文件类型
            long fileLength = file.getSize();
            logger.info("文件大小:"+fileLength);
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }
            // 分块 号码的范围是1~10000。如果超出这个范围,OSS将返回InvalidArgument的错误码。
            if (partCount > 10000) {
                throw new RuntimeException("文件过大(分块大小不能超过10000)");
            } else {
                logger.info("一共分了 " + partCount + " 块");
            }
            // 遍历分片上传。
            for (int i = 0; i < partCount; i++) {
                executor.execute(new PartUploader(i,partSize,partCount,file,fileLength,aliyunOSS,objectName,uploadId,ossClient,partETags));
            }
            // 等待所有分片完毕,关闭线程池(线程池不马上关闭)
            // 执行以前提交的任务,但不接受新任务。
            executor.shutdown();
            // 如果关闭后所有任务都已完成,则返回 true。
            while (!executor.isTerminated()) {
                try {
                    // 用于等待子线程结束,再继续执行下面的代码
                    executor.awaitTermination(5, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //验证是否所有的分片都完成
            if(partETags.size()!=partCount){
                throw new IllegalStateException("文件的某些部分上传失败!");
            }else {
                logger.info("成功上传文件:{}",file.getOriginalFilename());
            }
            /**完成分片上传**/
            //排序。partETags必须按分片号升序排列
            Collections.sort(partETags, new Comparator<PartETag>() {
                @Override
                public int compare(PartETag o1, PartETag o2) {
                    return o1.getPartNumber()-o2.getPartNumber();
                }
            });
            // 创建CompleteMultipartUploadRequest对象。
            // 在执行完成分片上传操作时,需要提供所有有效的partETags。
            // OSS收到提交的partETags后,会逐一验证每个分片的有效性。
            // 当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
            CompleteMultipartUploadRequest completeUpload = new CompleteMultipartUploadRequest(aliyunOSS.getBucketName(), objectName, uploadId, partETags);
            // 完成上传。
            ossClient.completeMultipartUpload(completeUpload);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return "上传文件异常!";
        } finally {
            if (ossClient!=null){
                // 关闭OSSClient。
                ossClient.shutdown();
            }
        }
        long endTime = System.currentTimeMillis();
        float time= (endTime - startTime) / 1000f;
        logger.info("执行时间为:"+time+"s");
        return aliyunOSS.getUrl()+objectName;
    }

    /**
     * 删除文件
     * @param key
     */
    @Override
    public boolean deleteFile(String key){
        // 判断文件
        if (StringUtils.isNotEmpty(key)) {
            OSSClient client = new OSSClient(aliyunOSS.getEndpoint(), aliyunOSS.getAccessKeyId(), aliyunOSS.getAccessKeySecret());
            try {
                if (!client.doesBucketExist(aliyunOSS.getBucketName())){
                    logger.info("Bucket不存在");
                    return false;
                }
                client.deleteObject(aliyunOSS.getBucketName(), key);
                logger.info("<----------OSS文件删-------->\n" + key);
            }catch (Exception e){
                e.printStackTrace();
                logger.info("删除失败");
                return false;
            }finally {
                if (client != null) {
                    client.shutdown();
                }
            }
        }
        return true;
    }


    /**实现并启动线程**/
    private static class PartUploader implements Runnable {
        private int i;//循环数
        private long partSize; //分块大小
        private int partCount; //分片总数
        private MultipartFile file;  //文件
        private long fileLength;//文件大小
        private AliyunOSS aliyunOSS; //上传OS配置
        private String objectName;//文件名
        private String uploadId;//它是分片上传事件的唯一标识,
        private OSS ossClient;//阿里云OSS调用对象
        private List<PartETag> partETags; // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。

        public PartUploader(int i, long partSize, int partCount, MultipartFile file, long fileLength, AliyunOSS aliyunOSS, String objectName, String uploadId, OSS ossClient, List<PartETag> partETags) {
            this.i = i;
            this.partSize = partSize;
            this.partCount = partCount;
            this.file = file;
            this.fileLength = fileLength;
            this.aliyunOSS = aliyunOSS;
            this.objectName = objectName;
            this.uploadId = uploadId;
            this.ossClient = ossClient;
            this.partETags = partETags;
        }

        @Override
        public void run() {
            logger.info("\n上传当前分片数:"+(i+1));
            InputStream instream=null;
            try{
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                instream = file.getInputStream();
                // 跳过已经上传的分片。
                instream.skip(startPos);
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(aliyunOSS.getBucketName());
                uploadPartRequest.setKey(objectName);
                uploadPartRequest.setUploadId(uploadId);
                uploadPartRequest.setInputStream(instream);
                // 设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100 KB。
                uploadPartRequest.setPartSize(curPartSize);
                // 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出这个范围,OSS将返回InvalidArgument的错误码。
                uploadPartRequest.setPartNumber(i + 1);
                // 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                //每次上传分片之后,OSS的返回结果会包含一个PartETag。PartETag将被保存到PartETags中。
                synchronized (this.partETags) {
                    this.partETags.add(uploadPartResult.getPartETag());
                }
            } catch (Exception e) {
                logger.error(e.getMessage());
            } finally {
                if(instream!=null){
                    try {
                        instream.close();
                    } catch (IOException e) {
                        logger.error(e.getMessage());
                    }
                }
            }
        }
    }
    private String getFileName(MultipartFile file) {
        //获取文件名
        String filename = file.getOriginalFilename();
        //获取最后一个.的位置
        int lastIndexOf = filename.lastIndexOf(".");
        //获取文件的后缀名
        String suffix = filename.substring(lastIndexOf);
        //上传到七牛的文件名
        return UUID.randomUUID().toString()+suffix;
    }
}

8.最后创建comtroller

package cc.flysoft.m.controller;

import cc.flysoft.m.service.AliyunOSSService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

/**
 * @author zhanglin
 * @descibe oss
 * @date 2020/5/27 13:19
 */
@RestController
@Slf4j
public class UpLoadController {

    @Autowired
    private AliyunOSSServiceImpl aliyunOSSService;

    /**
     * 文件上传
     */
    @RequestMapping(value = "/flyout/uploadFile")
    public String uploadBlog(@RequestParam("file") MultipartFile file) {
        String filename = file.getOriginalFilename();
        System.out.println(filename + "==filename");
        if (file != null) {
            if (!"".equals(filename.trim())) {
                // 上传到OSS
                return aliyunOSSService.fragmentUpload(file);
            }

        }
        return null;
    }
}

全部代码已经贴出,前端页面需要自己取写。

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
SpringBoot可以通过整合阿里云OSS对象存储服务来实现文件上传和管理功能。具体实现可以参考以下步骤: 1. 在service层定义FileService接口,该接口包含上传文件阿里云OSS的方法。例如,可以使用MultipartFile作为参数,返回上传成功后的文件URL。 2. 在controller层编写FileApiController类,该类使用@RestController注解标识为控制器,并使用@RequestMapping注解指定请求路径。在该类中,通过@Autowired注入FileService,并在文件上传的接口方法中调用FileService的上传文件方法并返回上传成功后的文件URL。 3. 在配置文件中配置阿里云OSS的相关信息,包括accessKey、secretKey、bucketName等。可以使用SpringBoot提供的@ConfigurationProperties注解来读取配置文件中的信息。 4. 在pom.xml文件中添加阿里云OSS SDK的依赖。 5. 编写上传文件的前端界面,可以使用HTML或者前端框架如Vue.js、React等。 通过以上步骤的实现,SpringBoot就可以整合阿里云OSS对象存储服务,实现文件上传和管理功能。这样可以将文件存储在阿里云OSS中,提高文件的安全性和可靠性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringBoot整合阿里云OSS对象存储服务的实现](https://download.csdn.net/download/weixin_38649091/12721580)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [全网最详细SpringBootSpringCloud整合阿里云OSS对象存储服务](https://blog.csdn.net/weixin_55076626/article/details/127924003)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熬菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值