|
@@ -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.common.config.security.utils.SecurityUtil;
|
|
|
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;
|
|
@@ -20,8 +21,11 @@ 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.redisson.api.RLock;
|
|
|
+import org.redisson.api.RedissonClient;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.context.annotation.Lazy;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
@@ -31,6 +35,7 @@ import java.util.ArrayList;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
@@ -43,6 +48,9 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
|
|
|
@Value("${douyin.tos.domain}")
|
|
|
private String DOUYIN_ACCESSIBLE_DOMAIN;
|
|
|
|
|
|
+ @Lazy
|
|
|
+ @Autowired
|
|
|
+ RedissonClient redissonClient;
|
|
|
@Autowired
|
|
|
private HttpRequestUtil httpRequestUtil;
|
|
|
@Autowired
|
|
@@ -150,61 +158,68 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
|
|
|
}
|
|
|
|
|
|
// 2.上传分块
|
|
|
- // - 抖音云单个分块大小不能小于4MB
|
|
|
+ // - 单个分块大小不能小于4MB
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public Map<String, Object> multipartUpload(MultipartFile multipartFile, String upload_id, Integer upload_chunk_index) {
|
|
|
|
|
|
- if (multipartFile.isEmpty()) throw new CustException("file 上传文件不能为空");
|
|
|
- if (StrUtil.isEmpty(upload_id)) throw new CustException("upload_id 不能为空");
|
|
|
- if (upload_chunk_index == null) throw new CustException("upload_chunk_index 不能为空");
|
|
|
-
|
|
|
- // [Get] 获得初始化分块信息
|
|
|
- SysFile sysFileEntity = sysFileDao.selectOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUpload_id, upload_id));
|
|
|
- if (sysFileEntity == null) throw new CustException("分块记录不存在");
|
|
|
- String object_key = sysFileEntity.getObject_key();
|
|
|
- Integer upload_chunk_size = sysFileEntity.getUpload_chunk_count();
|
|
|
- if (upload_chunk_index > upload_chunk_size) throw new CustException("分块索引(index)不能大于分块数量(count)");
|
|
|
-
|
|
|
- if (upload_chunk_index < upload_chunk_size) {
|
|
|
- // 分片编号从 1 开始,最大为 10000。除最后一个分片以外,其他分片大小最小为 4MiB
|
|
|
- if (multipartFile.getSize() < 4 * 1024 * 1024) {
|
|
|
- throw new CustException("分块文件大小不能小于 4MB");
|
|
|
+ // 按用户加锁
|
|
|
+ RLock lock = redissonClient.getLock("multipartUpload:" + SecurityUtil.getUserId());
|
|
|
+ try { lock.tryLock(3, TimeUnit.SECONDS);
|
|
|
+
|
|
|
+ if (multipartFile.isEmpty()) throw new CustException("file 上传文件不能为空");
|
|
|
+ if (StrUtil.isEmpty(upload_id)) throw new CustException("upload_id 不能为空");
|
|
|
+ if (upload_chunk_index == null) throw new CustException("upload_chunk_index 不能为空");
|
|
|
+
|
|
|
+ // [Get] 获得初始化分块信息
|
|
|
+ SysFile sysFileEntity = sysFileDao.selectOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUpload_id, upload_id));
|
|
|
+ if (sysFileEntity == null) throw new CustException("分块记录不存在");
|
|
|
+ String object_key = sysFileEntity.getObject_key();
|
|
|
+ Integer upload_chunk_size = sysFileEntity.getUpload_chunk_count();
|
|
|
+ if (upload_chunk_index > upload_chunk_size) throw new CustException("分块索引(index)不能大于分块数量(count)");
|
|
|
+
|
|
|
+ if (upload_chunk_index < upload_chunk_size) {
|
|
|
+ // 分片编号从 1 开始,最大为 10000。除最后一个分片以外,其他分片大小最小为 4MiB
|
|
|
+ if (multipartFile.getSize() < 4 * 1024 * 1024) {
|
|
|
+ throw new CustException("分块文件大小不能小于 4MB");
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // 获得公共配置
|
|
|
- List<SysCommon> sysCommonList = sysCommonService.getCommonByCategory("UPLOAD");
|
|
|
- AtomicReference<Integer> UPLOAD_TARGET = new AtomicReference<>();
|
|
|
- sysCommonList.stream().forEach(sysCommon -> {
|
|
|
- if (sysCommon.getTag().equals("UPLOAD_TARGET")) UPLOAD_TARGET.set(Convert.toInt(sysCommon.getValue()));
|
|
|
- });
|
|
|
- System.out.println("[系统配置] 上传目标(-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云): " + UPLOAD_TARGET);
|
|
|
+ // 获得公共配置
|
|
|
+ List<SysCommon> sysCommonList = sysCommonService.getCommonByCategory("UPLOAD");
|
|
|
+ AtomicReference<Integer> UPLOAD_TARGET = new AtomicReference<>();
|
|
|
+ sysCommonList.stream().forEach(sysCommon -> {
|
|
|
+ if (sysCommon.getTag().equals("UPLOAD_TARGET")) UPLOAD_TARGET.set(Convert.toInt(sysCommon.getValue()));
|
|
|
+ });
|
|
|
+ System.out.println("[系统配置] 上传目标(-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云): " + UPLOAD_TARGET);
|
|
|
|
|
|
|
|
|
- String 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().getETag();
|
|
|
- }
|
|
|
- // 3: 抖音云
|
|
|
- if (UPLOAD_TARGET.get() == 3) {
|
|
|
- UploadPartV2Output partResult = douyinTosService.uploadPart(multipartFile, upload_id, object_key, upload_chunk_index);
|
|
|
- part_etag = partResult.getEtag().replace("\"", "");
|
|
|
- }
|
|
|
+ // 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().getETag();
|
|
|
+ }
|
|
|
+ // 3: 抖音云
|
|
|
+ if (UPLOAD_TARGET.get() == 3) {
|
|
|
+ UploadPartV2Output partResult = douyinTosService.uploadPart(multipartFile, upload_id, object_key, upload_chunk_index);
|
|
|
+ part_etag = partResult.getEtag().replace("\"", "");
|
|
|
+ }
|
|
|
|
|
|
- // [Update] 更新分块文件信息
|
|
|
- sysFileEntity.setUpload_chunk_index(upload_chunk_index);
|
|
|
- sysFileDao.updateById(sysFileEntity);
|
|
|
+ // [Update] 更新分块文件信息
|
|
|
+ sysFileEntity.setUpload_chunk_index(upload_chunk_index);
|
|
|
+ sysFileDao.updateById(sysFileEntity);
|
|
|
|
|
|
- Map<String, Object> resp = new LinkedHashMap<>();
|
|
|
- resp.put("upload_chunk_index", upload_chunk_index);
|
|
|
- resp.put("upload_id", upload_id);
|
|
|
- resp.put("object_key", object_key);
|
|
|
- resp.put("part_etag", part_etag);
|
|
|
- return resp;
|
|
|
+ Map<String, Object> resp = new LinkedHashMap<>();
|
|
|
+ resp.put("upload_chunk_index", upload_chunk_index);
|
|
|
+ resp.put("upload_id", upload_id);
|
|
|
+ resp.put("object_key", object_key);
|
|
|
+ resp.put("part_etag", part_etag);
|
|
|
+ return resp;
|
|
|
+
|
|
|
+ } catch (InterruptedException e) { throw new RuntimeException(e);
|
|
|
+ } finally { lock.unlock(); }
|
|
|
}
|
|
|
|
|
|
// 完成分块上传
|