• 文件上传,你还存储在应用服务器?



    前言

    嗨,大家好,我是希留,一个被迫致力于全栈开发的老菜鸟。

    一般项目开发中都会有文件、图片、视频等文件上传并能够访问的场景。要实现这样的场景,要么把文件存储在应用服务器上,要么搭建文件服务来存储。但是这两种方式也有不少的缺点,增加运维的成本。

    因此,追求用户体验的项目可能会考虑使用第三方的云服务来实现存储,目前市场上主流的厂商有:七牛云、阿里云OSS、腾讯云COS等,具体采用哪种存储方案还需结合项目的规模、成本等因素,综合考量。

    因为我的是腾讯云的服务器,所以就直接开通了腾讯云的COS对象存储。这篇文章就来记录一下,SpringBoot整合腾讯云COS对象存储实现文件上传的。


    一、准备工作

    整合前需要做一些准备工作,开通COS对象存储,创建存储通,创建访问密钥。

    1. 开通腾讯云对象存储服务

    新用户专享,1米可以购买1年的存储。用来学习还是很香的。https://console.cloud.tencent.com/cos
    在这里插入图片描述

    2. 创建存储桶

    注意存储桶访问的访问权限,设置成公有读私有写。
    在这里插入图片描述
    在这里插入图片描述

    3. 密钥管理,创建密钥

    点击新建密钥,会自动生成。
    在这里插入图片描述
    在这里插入图片描述

    三、整合步骤

    1. 添加maven依赖

    代码如下(示例):

    <!--腾讯云 COS 对象存储-->
    <dependency>
    	<groupId>com.qcloud</groupId>
    	<artifactId>cos_api</artifactId>
    	<version>5.6.89</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2. yml文件增加配置

    代码如下(示例):

    # 腾讯云cos配置
    cos:
      baseUrl: https://xiliu-******24.cos.ap-guangzhou.myqcloud.com
      secretId: AKI******************ior
      secretKey: zZ*************z6
      regionName: ap-guangzhou
      bucketName: xiliu-******24
      folderPrefix: /upload
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3. 新建 COS 配置类

    代码如下(示例):

    package com.java.xiliu.config;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    /**
     * @author xiliu
     * @description 腾讯云cos对象存储配置类
     * @date 2022/10/28
     */
    @Data
    @Component
    @ConfigurationProperties(prefix = "cos")
    public class CosConfig {
        /**
         * 存储桶访问路径
         **/
        private String baseUrl;
        /**
         * 腾讯云账号秘钥
         **/
        private String secretId;
        /**
         * 密码秘钥
         **/
        private String secretKey;
        /**
         * 存储桶地区
         **/
        private String regionName;
        /**
         * 存储桶名称
         **/
        private String bucketName;
        /**
         * 上传的根目录
         **/
        private String folderPrefix;
    
        public COSClient getCosClient() {
            // 初始化用户信息
            COSCredentials cosCredentials = new BasicCOSCredentials(this.secretId,this.secretKey);
            // 设置地域
            Region region = new Region(this.regionName);
            ClientConfig config = new ClientConfig(region);
            // 生成COS客户端
            COSClient client = new COSClient(cosCredentials,config);
            return client;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    4. 新建 COS 上传工具类

    代码如下(示例):

    package com.java.xiliu.common.untils.file;
    
    import com.java.xiliu.common.untils.SpringUtils;
    import com.java.xiliu.config.CosConfig;
    import com.qcloud.cos.COSClient;
    import com.qcloud.cos.model.PutObjectRequest;
    import com.qcloud.cos.model.UploadResult;
    import com.qcloud.cos.transfer.TransferManager;
    import com.qcloud.cos.transfer.Upload;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.io.FilenameUtils;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author xiliu
     * @description 腾讯云COS文件上传工具类
     * @date 2022/10/31
     */
    @Slf4j
    public class CosClientUtils {
    
        /**
         * 获取配置信息
         */
        private static CosConfig cosConfig = SpringUtils.getBean(CosConfig.class);
    
        public static String upload(MultipartFile file, String dir) throws Exception {
            String originalFilename = file.getOriginalFilename();
            // 文件名
            String name = FilenameUtils.getBaseName(originalFilename) + "_" + System.currentTimeMillis() + originalFilename.substring(originalFilename.lastIndexOf("."));
            // 目录
            String folderName = cosConfig.getFolderPrefix() + "/" + dir + "/";
            String key = folderName + name;
            File localFile = null;
            try {
                localFile = transferToFile(file);
                String filePath = uploadFileToCos(localFile, key);
                log.info("upload COS successful: {}", filePath);
                return filePath;
            } catch (Exception e) {
                throw new Exception("文件上传失败");
            } finally {
                localFile.delete();
            }
        }
    
        /**
         * 用缓冲区来创建临时文件
         * 使用 MultipartFile.transferTo()
         * @param multipartFile
         * @return
         */
        private static File transferToFile(MultipartFile multipartFile) throws IOException {
            String originalFilename = multipartFile.getOriginalFilename();
            String prefix = originalFilename.split("\\.")[0];
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            File file = File.createTempFile(prefix, suffix);
            multipartFile.transferTo(file);
            return file;
        }
    
        /**
         * 上传文件到COS
         * @param localFile
         * @param key
         * @return
         */
        private static String uploadFileToCos(File localFile, String key) throws InterruptedException {
            PutObjectRequest putObjectRequest = new PutObjectRequest(cosConfig.getBucketName(), key, localFile);
            // 获取连接
            COSClient cosClient = cosConfig.getCosClient();
            // 创建线程池
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(8, 16,
                    4, TimeUnit.SECONDS, new ArrayBlockingQueue(10), new ThreadPoolExecutor.AbortPolicy());
            // 传入一个threadPool, 若不传入线程池, 默认TransferManager中会生成一个单线程的线程池
            TransferManager transferManager = new TransferManager(cosClient, threadPool);
            // 返回一个异步结果Upload, 可同步的调用waitForUploadResult等待upload结束, 成功返回UploadResult, 失败抛出异常
            Upload upload = transferManager.upload(putObjectRequest);
            UploadResult uploadResult = upload.waitForUploadResult();
            transferManager.shutdownNow();
            cosClient.shutdown();
            String filePath = cosConfig.getBaseUrl() + uploadResult.getKey();
            return filePath;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    5. 新建 Controller 上传接口

    代码如下(示例):

        @ApiOperation(value = "用户头像上传")
        @PostMapping("/profile/avatar")
        public R uploadAvatar(@RequestParam("avatarfile") MultipartFile file) throws Exception {
            if (!file.isEmpty()) {
                String avatar = CosClientUtils.upload(file, "avatar");
                return R.ok(avatar);
            }
            return R.error("上传头像异常,请联系管理员");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    6. 测试

    使用 Swagger 接口文档或者Postman测试上传接口,成功返回地址。使用浏览器打开该返回地址,可以正常预览。
    在这里插入图片描述
    在这里插入图片描述


    总结

    以上就是本文的全部内容了,感谢大家的阅读。

    如果觉得文章对你有帮助,还不忘帮忙点赞、收藏、关注、评论哟,您的支持就是我创作最大的动力!

  • 相关阅读:
    每天一点python——day63
    《你好,放大器》----学习记录(六)
    027-从零搭建微服务-搜索服务(一)
    【蓝桥杯】赢球票(模拟、枚举、搜索)
    SEO外语网站批量翻译软件
    Flutter(一) package的使用、开发与发布
    接口测试--Postman常用断言
    linux安装scrcpy最新版本
    Alpha-Beta剪枝的原理的深入理解(无图预警)
    java计算机毕业设计基于springboot大学生招聘简历投递系统
  • 原文地址:https://blog.csdn.net/qq_30859353/article/details/127573866