tsurumure 4 ماه پیش
والد
کامیت
71750c409f

+ 15 - 1
src/main/java/com/backendsys/modules/sdk/douyincloud/tos/service/DouyinTosService.java

@@ -1,20 +1,34 @@
 package com.backendsys.modules.sdk.douyincloud.tos.service;
 
 import com.backendsys.modules.upload.entity.SysFileResult;
+import com.qcloud.cos.model.PartETag;
+import com.volcengine.tos.model.object.CompleteMultipartUploadV2Output;
+import com.volcengine.tos.model.object.CreateMultipartUploadOutput;
+import com.volcengine.tos.model.object.UploadPartV2Output;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
+import java.util.List;
 
 /**
  * 文档首页/对象存储/SDK参考/Java/对象接口/上传对象/普通上传(Java SDK)
  * https://www.volcengine.com/docs/6349/79898
+ * 分片上传
+ * https://www.volcengine.com/docs/6349/79922
  */
 public interface DouyinTosService {
 
     // [抖音云TOS] 上传对象
     SysFileResult putObject(MultipartFile multipartFile, String object_key) throws IOException;
-
     // [抖音云TOS] 删除对象
     void deleteObject(String object_key) throws IOException;
 
+
+    // [抖音云TOS] 初始化分块上传
+    CreateMultipartUploadOutput initiateMultipartUpload(MultipartFile multipartFile);
+    // [抖音云TOS] 上传分块
+    UploadPartV2Output uploadPart(MultipartFile multipartFile, String upload_id, String object_key, Integer partNumber);
+    // [抖音云TOS] 完成分块上传
+    CompleteMultipartUploadV2Output completeMultipartUpload(String upload_id, String object_key);
+
 }

+ 98 - 0
src/main/java/com/backendsys/modules/sdk/douyincloud/tos/service/impl/DouyinTosServiceImpl.java

@@ -12,6 +12,8 @@ import com.backendsys.modules.sse.entity.SseResponse;
 import com.backendsys.modules.sse.entity.SseResponseEnum;
 import com.backendsys.modules.sse.utils.SseUtil;
 import com.backendsys.modules.upload.entity.SysFileResult;
+import com.qcloud.cos.exception.CosClientException;
+import com.qcloud.cos.model.PartETag;
 import com.volcengine.tos.TOSV2;
 import com.volcengine.tos.TOSV2ClientBuilder;
 import com.volcengine.tos.TosClientException;
@@ -28,6 +30,7 @@ import org.springframework.web.multipart.MultipartFile;
 import java.io.IOException;
 import java.io.InputStream;
 import java.math.BigDecimal;
+import java.util.List;
 
 // 对象接口 -> 上传对象
 // https://www.volcengine.com/docs/6349/79898
@@ -180,6 +183,7 @@ public class DouyinTosServiceImpl implements DouyinTosService {
     /**
      * [抖音云COS] 删除对象
      */
+    @Override
     public void deleteObject(String object_key) {
         try {
             // 初始化 TosClient
@@ -193,4 +197,98 @@ public class DouyinTosServiceImpl implements DouyinTosService {
         }
     }
 
+
+    /**
+     * [抖音云TOS] 初始化分块上传
+     */
+    @Override
+    public CreateMultipartUploadOutput initiateMultipartUpload(MultipartFile multipartFile) {
+        TOSV2 tos = new TOSV2ClientBuilder().build(REGION, ENDPOINT, ACCESS_KEY_ID, SECRET_ACCESS_KEY);
+        try {
+
+            // 使用临时文件夹 (/temp) + UUID文件名
+            String filename = CommonUtil.generateUUIDFilename(multipartFile);
+            String object_key = "temp/" + filename;
+            System.out.println("InitiateMultipartUpload object: " + object_key);
+
+            // 初始化分片上传
+            CreateMultipartUploadInput create = new CreateMultipartUploadInput().setBucket(BUCKET_NAME).setKey(object_key);
+            CreateMultipartUploadOutput createOutput = tos.createMultipartUpload(create);
+            System.out.println("CreateMultipartUpload succeed. UploadId is: " + createOutput.getUploadID());
+            return createOutput;
+
+        } catch (TosClientException | TosServerException e) {
+            // 操作失败,捕获客户端异常,一般情况是请求参数错误,此时请求并未发送
+            throw new CustException("TosClientException/TosServerException: " + e.getMessage());
+        } catch (Throwable t) {
+            // 作为兜底捕获其他异常,一般不会执行到这里
+            throw new CustException("Throwable (2): " + t.getMessage());
+        }
+    }
+
+    /**
+     * [抖音云TOS] 上传分块
+     */
+    @Override
+    public UploadPartV2Output uploadPart(MultipartFile multipartFile, String upload_id, String object_key, Integer partNumber) {
+        InputStream inputStream = null;
+        TOSV2 tos = new TOSV2ClientBuilder().build(REGION, ENDPOINT, ACCESS_KEY_ID, SECRET_ACCESS_KEY);
+        try {
+
+            inputStream = multipartFile.getInputStream();
+            UploadPartV2Input input = new UploadPartV2Input()
+                .setBucket(BUCKET_NAME)
+                .setKey(object_key)
+                .setUploadID(upload_id)
+                .setPartNumber(partNumber)
+                .setContentLength(multipartFile.getSize())
+                .setContent(inputStream);
+            UploadPartV2Output output = tos.uploadPart(input);
+            return output;
+
+        } catch (IOException | CosClientException | TosClientException | TosServerException e) {
+            throw new CustException(e.getMessage());
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    // 忽略关闭流时的异常
+                    throw new CustException(e.getMessage());
+                }
+            }
+        }
+    }
+
+
+    /**
+     * [抖音云TOS] 完成分块上传
+     */
+    @Override
+    public CompleteMultipartUploadV2Output completeMultipartUpload(String upload_id, String object_key) {
+
+        if (StrUtil.isEmpty(upload_id)) throw new CustException("upload_id 不能为空");
+        if (StrUtil.isEmpty(object_key)) throw new CustException("object_key 不能为空");
+
+        TOSV2 tos = new TOSV2ClientBuilder().build(REGION, ENDPOINT, ACCESS_KEY_ID, SECRET_ACCESS_KEY);
+        try {
+
+            CompleteMultipartUploadV2Input complete = new CompleteMultipartUploadV2Input()
+                .setBucket(BUCKET_NAME)
+                .setKey(object_key)
+                .setUploadID(upload_id);
+
+            complete.setCompleteAll(true);
+
+            CompleteMultipartUploadV2Output completedOutput = tos.completeMultipartUpload(complete);
+            return completedOutput;
+
+        } catch (TosClientException | TosServerException e) {
+            throw new CustException(e.getMessage());
+        } catch (Throwable t) {
+            throw new CustException(t.getMessage());
+        }
+
+    }
+
 }

+ 4 - 9
src/main/java/com/backendsys/modules/sdk/tencentcloud/cos/service/TencentCosService.java

@@ -12,29 +12,24 @@ public interface TencentCosService {
 
     // [腾讯云COS] 上传对象
     SysFileResult putObject(MultipartFile multipartFile, String object_key) throws IOException;
-
     // [腾讯云COS] 删除对象
     void deleteObject(String object_key);
 
+
 //    // [腾讯云COS] 批量删除对象
 //    void deleteObjects(String object_keys);
-
-
     // [腾讯云COS] 查询对象是否存在
     boolean doesObjectExist(String object_key);
-
     // [腾讯云COS] 获得临时图片地址
     URL generateUrl(String object_key, Integer minutes);
 
+
     // [腾讯云COS] 初始化分块上传
     InitiateMultipartUploadResult initiateMultipartUpload(MultipartFile multipartFile);
-
     // [腾讯云COS] 上传分块
     UploadPartResult uploadPart(MultipartFile multipartFile, String upload_id, String object_key, Integer partNumber);
-
-    // [腾讯云COS] 查询已上传的分块
-    PartListing listParts(String upload_id, String object_key);
-
     // [腾讯云COS] 完成分块上传
     CompleteMultipartUploadResult completeMultipartUpload(String upload_id, String object_key, List<PartETag> partETags);
+    // [腾讯云COS] 查询已上传的分块
+    PartListing listParts(String upload_id, String object_key);
 }

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

@@ -310,9 +310,6 @@ public class TencentCosServiceImpl implements TencentCosService {
         if (StrUtil.isEmpty(upload_id)) throw new CustException("upload_id 不能为空");
         if (StrUtil.isEmpty(object_key)) throw new CustException("object_key 不能为空");
 
-
-
-
         COSClient cosClient = getClient();
         try {
 

+ 34 - 10
src/main/java/com/backendsys/modules/upload/service/impl/SysFileMultipartServiceImpl.java

@@ -7,6 +7,7 @@ import cn.hutool.core.util.StrUtil;
 import cn.hutool.crypto.digest.DigestUtil;
 import com.backendsys.exception.CustException;
 import com.backendsys.modules.common.config.security.utils.HttpRequestUtil;
+import com.backendsys.modules.sdk.douyincloud.tos.service.DouyinTosService;
 import com.backendsys.modules.sdk.tencentcloud.cos.service.TencentCosService;
 import com.backendsys.modules.system.entity.SysCommon;
 import com.backendsys.modules.system.service.SysCommonService;
@@ -16,6 +17,9 @@ import com.backendsys.modules.upload.enums.StyleEnums;
 import com.backendsys.modules.upload.service.SysFileMultipartService;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.qcloud.cos.model.*;
+import com.volcengine.tos.model.object.CompleteMultipartUploadV2Output;
+import com.volcengine.tos.model.object.CreateMultipartUploadOutput;
+import com.volcengine.tos.model.object.UploadPartV2Output;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
@@ -39,6 +43,8 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
     @Autowired
     private TencentCosService tencentCosService;
     @Autowired
+    private DouyinTosService douyinTosService;
+    @Autowired
     private SysCommonService sysCommonService;
     @Autowired
     private SysFileDao sysFileDao;
@@ -84,17 +90,22 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
 
         System.out.println("[系统配置] 上传目标(-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云): " + UPLOAD_TARGET);
 
-        String target_label = "";
+        String target_label = StyleEnums.targetToLabel(UPLOAD_TARGET.get());
         String upload_id = null;
         String object_key = null;
 
         // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
         if (UPLOAD_TARGET.get() == 1) {
-            target_label = "腾讯云初始化分块";
             InitiateMultipartUploadResult uploadResult = tencentCosService.initiateMultipartUpload(multipartFile);
             upload_id = uploadResult.getUploadId();
             object_key = uploadResult.getKey();
         }
+        // 3: 抖音云
+        if (UPLOAD_TARGET.get() == 3) {
+            CreateMultipartUploadOutput uploadResult = douyinTosService.initiateMultipartUpload(multipartFile);
+            upload_id = uploadResult.getUploadID();
+            object_key = uploadResult.getKey();
+        }
 
         // 如果没有获得以上参数,则抛出报错
         if (StrUtil.isEmpty(upload_id) || StrUtil.isEmpty(object_key)) {
@@ -149,12 +160,17 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
         System.out.println("[系统配置] 上传目标(-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云): " + UPLOAD_TARGET);
 
 
-        PartETag part_etag = null;
+        String part_etag = null;
 
         // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
         if (UPLOAD_TARGET.get() == 1) {
             UploadPartResult partResult = tencentCosService.uploadPart(multipartFile, upload_id, object_key, upload_chunk_index);
-            part_etag = partResult.getPartETag();
+            part_etag = partResult.getPartETag().getETag();
+        }
+        // 3: 抖音云
+        if (UPLOAD_TARGET.get() == 3) {
+            UploadPartV2Output partResult = douyinTosService.uploadPart(multipartFile, upload_id, object_key, upload_chunk_index);
+            part_etag = partResult.getEtag();
         }
 
 //        // 查询分块上传情况 (获得下次要上传的位置)
@@ -208,17 +224,15 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
             PartListing partListing = listParts(upload_id, sysFileEntity.getObject_key());
             List<PartSummary> partSummaryList = partListing.getParts();
             List<PartETag> etags = partSummaryList.stream()
-                    .map(partSummary -> new PartETag(partSummary.getPartNumber(), partSummary.getETag()))
-                    .collect(Collectors.toList());
+                .map(partSummary -> new PartETag(partSummary.getPartNumber(), partSummary.getETag()))
+                .collect(Collectors.toList());
 
             // 如果分块数量对不上,最好还是重新上传
-            if (sysFileEntity.getUpload_chunk_count() != etags.size()) {
-                throw new CustException("分块索引异常,请重新上传");
-            }
+            if (sysFileEntity.getUpload_chunk_count() != etags.size()) throw new CustException("分块索引异常,请重新上传");
 
             // [腾讯云] 合并分块
             CompleteMultipartUploadResult completeResult = tencentCosService.completeMultipartUpload(upload_id, sysFileEntity.getObject_key(), etags);
-            if (completeResult == null) throw new CustException("合并失败");
+            if (completeResult == null) throw new CustException("分块合并失败");
 
             // 拼接图片路径
             String url = ACCESSIBLE_DOMAIN + "/" + completeResult.getKey();
@@ -238,6 +252,16 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
             resp.put("url", url);
             return resp;
 
+        }
+
+        // 3: 抖音云
+        if (UPLOAD_TARGET.get() == 3) {
+
+            CompleteMultipartUploadV2Output completeResult = douyinTosService.completeMultipartUpload(upload_id, sysFileEntity.getObject_key());
+            if (completeResult == null) throw new CustException("分块合并失败");
+
+
+
         }
 
         return null;