Kaynağa Gözat

重构腾讯云cos接口

tsurumure 8 ay önce
ebeveyn
işleme
80a8d9833e

+ 29 - 0
db/sys_upload.sql

@@ -0,0 +1,29 @@
+/**
+Source Server Version: 8.0.31
+Source Database: backendsys
+Date: 2023/05/23 17:09:22
+*/
+
+DROP TABLE IF EXISTS `sys_upload`;
+
+CREATE TABLE `sys_upload` (
+    PRIMARY KEY (`id`),
+    `id` BIGINT(10) AUTO_INCREMENT COMMENT 'ID',
+    `category_id` BIGINT(10) COMMENT '分类ID',
+    `user_id` BIGINT(10) COMMENT '上传者',
+    `name` VARCHAR(255) NOT NULL COMMENT '文件名称',
+    `object_key` VARCHAR(500) COMMENT 'ObjectKey',
+    `file_local_path` VARCHAR(255) COMMENT '本地路径',
+    `file_remote_path` VARCHAR(255) COMMENT '远程路径',
+    `file_remote_path_thumb` VARCHAR(255) COMMENT '远程路径 (缩略图)',
+    `file_key` VARCHAR(255) COMMENT '腾讯云COS路径',
+    `size` INT COMMENT '文件大小',
+    `md5` VARCHAR(255) COMMENT '文件MD5',
+    `chunk_upload_id` VARCHAR(255) COMMENT '文件分片任务ID',
+    `chunk_count` INT COMMENT '文件分片数量',
+    `chunk_current_index` INT COMMENT '当前文件分片索引',
+    `notes` VARCHAR(255) COMMENT '备注',
+    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
+) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='系统文件表';
+

+ 22 - 0
db/sys_upload_category.sql

@@ -0,0 +1,22 @@
+/**
+Source Server Version: 8.0.31
+Source Database: backendsys
+Date: 2023/05/23 17:09:22
+*/
+
+DROP TABLE IF EXISTS `sys_upload_category`;
+
+CREATE TABLE `sys_upload_category` (
+    PRIMARY KEY (`id`),
+    `id` BIGINT(10) AUTO_INCREMENT COMMENT 'ID',
+    `category_name` VARCHAR(255) NOT NULL COMMENT '分类名称',
+    `suffix` VARCHAR(1000) NOT NULL COMMENT '文件后缀',
+    `sort` BIGINT(10) DEFAULT '1' COMMENT '排序',
+    `status` TINYINT(1) DEFAULT '1' COMMENT '资讯状态 (-1禁用, 1启用)'
+) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='系统文件分类表';
+
+INSERT INTO sys_upload_category(category_name, suffix) VALUES
+    ('图片', 'jpg, jpeg, png, gif, bmp, tiff, webp'),
+    ('视频', 'mp4, avi, mov, mkv, wmv, flv, webm'),
+    ('音频', 'mp3, wav, ogg, aac')
+;

+ 14 - 0
src/main/java/com/backendsys/modules/sdk/tencent/cos/service/TencentCosService.java

@@ -0,0 +1,14 @@
+package com.backendsys.modules.sdk.tencent.cos.service;
+
+import com.backendsys.entity.Tencent.TencentCos.MultipartUploadRespDTO;
+import com.qcloud.cos.model.UploadResult;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+
+public interface TencentCosService {
+
+    // [腾讯云COS] 上传对象
+    UploadResult putObject(MultipartFile file, String object_key) throws IOException;
+
+}

+ 211 - 0
src/main/java/com/backendsys/modules/sdk/tencent/cos/service/impl/TencentCosServiceImpl.java

@@ -0,0 +1,211 @@
+package com.backendsys.modules.sdk.tencent.cos.service.impl;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.digest.DigestUtil;
+import com.backendsys.entity.System.SysFile.SysFileCategoryDTO;
+import com.backendsys.entity.System.SysFile.SysFileDTO;
+import com.backendsys.entity.Tencent.TencentCos.MultipartUploadRespDTO;
+import com.backendsys.exception.CustException;
+import com.backendsys.modules.sdk.tencent.cos.service.TencentCosService;
+import com.backendsys.modules.sdk.tencent.cos.utils.TencentCosUtil;
+import com.backendsys.utils.CommonUtil;
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.auth.BasicCOSCredentials;
+import com.qcloud.cos.auth.COSCredentials;
+import com.qcloud.cos.model.PutObjectRequest;
+import com.qcloud.cos.model.UploadResult;
+import com.qcloud.cos.region.Region;
+import com.qcloud.cos.transfer.Transfer;
+import com.qcloud.cos.transfer.TransferManager;
+import com.qcloud.cos.transfer.TransferProgress;
+import com.qcloud.cos.transfer.Upload;
+import com.qcloud.cos.utils.IOUtils;
+import net.coobird.thumbnailator.Thumbnails;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+@Service
+public class TencentCosServiceImpl implements TencentCosService {
+
+    @Value("${tencent.cos.max-size}")
+    private long MAX_SIZE;
+    @Value("${tencent.cos.region}")
+    private String REGION;
+    @Value("${tencent.cos.bucket-name}")
+    private String BUCKET_NAME;
+    @Value("${tencent.cos.secret-id}")
+    private String SECRET_ID;
+    @Value("${tencent.cos.secret-key}")
+    private String SECRET_KEY;
+
+    // [腾讯云COS] 生成cos客户端
+    private COSClient getClient() {
+        COSCredentials cred = new BasicCOSCredentials(SECRET_ID, SECRET_KEY);
+        Region region1 = new Region(REGION);
+        ClientConfig clientConfig = new ClientConfig(region1);
+        clientConfig.setRegion(region1);
+        clientConfig.setPrintShutdownStackTrace(false);
+        COSClient cosClient = new COSClient(cred, clientConfig);
+        return cosClient;
+    }
+
+    // [腾讯云COS] 上传对象
+    @Override
+    public UploadResult putObject(MultipartFile file, String object_key) {
+
+        if (file.isEmpty()) throw new CustException("file 上传文件不能为空");
+
+        COSClient cosClient = getClient();
+        try {
+
+            InputStream inputStream = file.getInputStream();
+
+            // 将文件内容写入临时文件
+            String tempFilename = CommonUtil.generateFilename(null);
+            File tempFile = File.createTempFile(tempFilename, null);
+            try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
+                IOUtils.copy(inputStream, outputStream);
+            }
+
+            // 如果不传 object_key,则默认使用临时文件夹 (/temp) + MD5文件名
+            if (StrUtil.isEmpty(object_key)) {
+                String md5 = DigestUtil.md5Hex(file.getInputStream());
+                String suffix = FileUtil.extName(file.getOriginalFilename()).toLowerCase();
+                String filename = md5 + "." + suffix;
+                object_key = "temp/" + filename;
+            }
+
+            // 创建 PutObjectRequest 对象,PutObject 请求。(异步)
+            PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, object_key, tempFile);
+//            cosClient.putObject(putObjectRequest);   // 普通上传 (没有进度)
+
+            // 高级上传
+            TransferManager transferManager = TencentCosUtil.createTransferManager(cosClient);
+            Upload upload = transferManager.upload(putObjectRequest);   // 返回一个异步结果Upload
+            TencentCosUtil.showTransferProgress(upload);                // 打印上传进度,直到上传结束
+            UploadResult uploadResult = upload.waitForUploadResult();   // 捕获可能出现的异常
+            return uploadResult;
+
+        } catch (IOException e) {
+            throw new CustException(e.getMessage());
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (cosClient != null) cosClient.shutdown();
+        }
+
+//
+//        MultipartUploadRespDTO resp = new MultipartUploadRespDTO();
+//        try {
+//
+//            String md5 = DigestUtil.md5Hex(file.getInputStream());
+//            String suffix = FileUtil.extName(file.getOriginalFilename()).toLowerCase();
+//            String filename = md5 + "." + suffix;
+//            String filenameThumb = filename.replaceAll("." + suffix, "-thumb." + suffix);
+//            String key = "temp/" + filename;
+//            String keyThumb = "temp/" + filenameThumb;
+//            String remotePath = accessibleDomain + "/" + key;
+//            String remotePathThumb = accessibleDomain + "/" + keyThumb;
+//
+//            // 获得文件分类列表
+//            SysFileCategoryDTO categoryDTO = new SysFileCategoryDTO();
+//            List<SysFileCategoryDTO> categoryList = sysFileCategoryMapper.queryFileCategoryList(categoryDTO);
+//
+//            // 获得图片分类的后缀 (待做),再进行以下判断
+//
+//            // 还有删除也要同时删除缩略图
+//
+//            // 还有大文件分片也要做缩略图
+//
+//            // 还有 视频要做截图生成
+//
+//            boolean isImage = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp").contains(suffix.toLowerCase());
+//
+//
+//            // 检查对象是否存在于存储桶中
+//            if (checkObjectExist(cosClient, key)) {
+//                // -- [COS] 对象存在时,直接返回路径 -------------------------------
+//                resp.setIs_fast_upload(true);
+//
+//            } else {
+//                // 获取文件内容
+//                InputStream inputStream = file.getInputStream();
+//
+//                // 将文件内容写入临时文件
+//                String tempFilename = CommonUtil.generateFilename(null);
+//                File tempFile = File.createTempFile(tempFilename, null);
+//                try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
+//                    IOUtils.copy(inputStream, outputStream);
+//                }
+//                // 创建 PutObjectRequest 对象,PutObject 请求。(异步)
+//                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, tempFile);
+//                cosClient.putObject(putObjectRequest);
+//
+//
+//                // 创建缩略图
+//                if (isImage) {
+//                    File tempFileThumb = File.createTempFile(tempFilename, "." + suffix);
+//                    String sourceFilePath = tempFileThumb.getPath();
+//                    File sourceFile = new File(sourceFilePath);
+//                    // 使用Thumbnailator生成缩略图并写入到临时文件
+//                    Thumbnails.of(tempFile).size(60, 60).outputQuality(0.2).toFile(tempFileThumb);
+//                    PutObjectRequest putObjectRequestThumb = new PutObjectRequest(bucketName, keyThumb, sourceFile);
+//                    cosClient.putObject(putObjectRequestThumb);
+//                    resp.setPath_thumb(remotePathThumb);
+//                    tempFileThumb.delete();
+//                }
+//
+//                tempFile.delete();
+//
+//            }
+//
+//            // -- [DB] 查询数据 ---------------------------------------------
+//            SysFileDTO fileDetail = sysFileMapper.queryFileByKey(key);
+//            // 如果表中没有数据,则添加数据
+//            if (fileDetail == null) {
+//                // -- [DB] 插入数据 -----------------------------------------
+//                SysFileDTO fileDTO = new SysFileDTO();
+//                fileDTO.setUser_id(httpRequestAspect.getUserId());
+//                fileDTO.setName(filename);
+//                fileDTO.setFile_key(key);
+//                fileDTO.setFile_remote_path(remotePath);
+//                if (isImage) {
+//                    fileDTO.setFile_remote_path_thumb(remotePathThumb);
+//                }
+//                fileDTO.setCategory_id(getCategoryIdBySuffix(suffix));
+//                fileDTO.setSize(file.getSize());
+//                fileDTO.setMd5(md5);
+//                sysFileMapper.insertFile(fileDTO);
+//            }
+//            // -------------------------------------------------------------
+//
+//            resp.setFilename(filename);
+//            resp.setKey(key);
+//            resp.setMd5(md5);
+//            resp.setSize(file.getSize());
+//            resp.setPath(remotePath);
+//
+//        } catch (IOException e) {
+//            throw new CustException(e.getMessage());
+//        } finally {
+//            if (cosClient != null) {
+//                cosClient.shutdown();
+//            }
+//        }
+//        return resp;
+
+    }
+
+}

+ 46 - 0
src/main/java/com/backendsys/modules/sdk/tencent/cos/utils/TencentCosUtil.java

@@ -0,0 +1,46 @@
+package com.backendsys.modules.sdk.tencent.cos.utils;
+
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.transfer.Transfer;
+import com.qcloud.cos.transfer.TransferManager;
+import com.qcloud.cos.transfer.TransferProgress;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class TencentCosUtil {
+
+    // [腾讯云COS][高级接口] 创建 TransferManager 实例
+    public static TransferManager createTransferManager(COSClient cosClient) {
+        // 自定义线程池大小,建议在客户端与 COS 网络充足(例如使用腾讯云的 CVM,同地域上传 COS)的情况下,设置成16或32即可,可较充分的利用网络资源
+        // 对于使用公网传输且网络带宽质量不高的情况,建议减小该值,避免因网速过慢,造成请求超时。
+        ExecutorService threadPool = Executors.newFixedThreadPool(32);
+        // 传入一个 threadpool, 若不传入线程池,默认 TransferManager 中会生成一个单线程的线程池。
+        return new TransferManager(cosClient, threadPool);
+    }
+
+
+    // [腾讯云COS][高级接口] 获取进度函数
+    public static void showTransferProgress(Transfer transfer) {
+        // 这里的 Transfer 是异步上传结果 Upload 的父类
+        System.out.println(transfer.getDescription());
+        // 查询上传是否已经完成
+        while (transfer.isDone() == false) {
+            try {
+                // 每 2 秒获取一次进度
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+                return;
+            }
+            TransferProgress progress = transfer.getProgress();
+            long sofar = progress.getBytesTransferred();
+            long total = progress.getTotalBytesToTransfer();
+            double pct = progress.getPercentTransferred();
+            System.out.printf("upload progress: [%d / %d] = %.02f%%\n", sofar, total, pct);
+        }
+        // 完成了 Completed,或者失败了 Failed
+        System.out.println(transfer.getState());
+    }
+
+
+}

+ 4 - 4
src/main/java/com/backendsys/modules/sdk/tencent/sms/service/impl/TencentSmsServiceImpl.java

@@ -25,7 +25,7 @@ public class TencentSmsServiceImpl implements TencentSmsService {
     @Value("${tencent.sms.sign-name}")
     private String SIGN_NAME;
 
-    // [腾讯云] 生成 SmsClient
+    // [腾讯云SMS] 生成 SmsClient
     private SmsClient getSmsClient() {
         Credential cred = new Credential(SECRET_ID, SECRET_KEY);
         ClientProfile clientProfile = new ClientProfile();
@@ -34,7 +34,7 @@ public class TencentSmsServiceImpl implements TencentSmsService {
     }
 
     /**
-     * [腾讯云] 发送短信 { 模板ID, 模板参数, 下发手机号码 }
+     * [腾讯云SMS] 发送短信 { 模板ID, 模板参数, 下发手机号码 }
      * https://cloud.tencent.com/document/product/382/55981
      */
     @Override
@@ -56,7 +56,7 @@ public class TencentSmsServiceImpl implements TencentSmsService {
     }
 
     /**
-     * [腾讯云] 获得统计短信发送数据
+     * [腾讯云SMS] 获得统计短信发送数据
      * https://cloud.tencent.com/document/product/382/55965
      */
     @Override
@@ -89,7 +89,7 @@ public class TencentSmsServiceImpl implements TencentSmsService {
     }
 
     /**
-     * [腾讯云] 获得回执数据统计
+     * [腾讯云SMS] 获得回执数据统计
      * https://cloud.tencent.com/document/product/382/55966
      */
     @Override

+ 37 - 0
src/main/java/com/backendsys/modules/upload/controller/UploadController.java

@@ -0,0 +1,37 @@
+package com.backendsys.modules.upload.controller;
+
+import com.backendsys.exception.CustException;
+import com.backendsys.modules.common.utils.Result;
+import com.backendsys.modules.sdk.tencent.cos.service.TencentCosService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+
+@Validated
+@RestController
+public class UploadController {
+
+    @Autowired
+    private TencentCosService tencentCosService;
+
+    /**
+     * 简单上传文件
+     */
+    @PreAuthorize("@ss.hasPermi(1.1)")
+    @PostMapping("/api/v2/upload/simpleUpload")
+    public Result simpleUpload(@RequestParam("file") MultipartFile file) throws IOException {
+
+        // 判断文件是否超过大小 (5MB = 5242880) (1GB = 1073741824)
+        long MAX_SIZE = 5242880;
+        if (file.getSize() > MAX_SIZE) throw new CustException("上传文件不能大于 " + MAX_SIZE/1024/1024 + " MB,请使用大文件上传功能");
+
+        return Result.success().put("data", tencentCosService.putObject(file, null));
+    }
+
+}