|
|
@@ -2,6 +2,7 @@ package com.inspur.source.service.impl;
|
|
|
|
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
@@ -13,33 +14,41 @@ import com.inspur.source.mapper.*;
|
|
|
import com.inspur.source.service.ISmsbMinioDataService;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.dromara.common.core.exception.ServiceException;
|
|
|
import org.dromara.common.core.utils.MapstructUtils;
|
|
|
import org.dromara.common.core.utils.StringUtils;
|
|
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
|
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
|
|
import org.dromara.common.oss.core.OssClient;
|
|
|
import org.dromara.common.oss.entity.UploadResult;
|
|
|
+import org.dromara.common.oss.enumd.AccessPolicyType;
|
|
|
import org.dromara.common.oss.factory.OssFactory;
|
|
|
+import org.dromara.common.redis.utils.RedisUtils;
|
|
|
import org.dromara.common.satoken.utils.LoginHelper;
|
|
|
import org.dromara.system.domain.SysOss;
|
|
|
import org.dromara.system.domain.vo.SysOssVo;
|
|
|
import org.dromara.system.mapper.SysOssMapper;
|
|
|
+import org.jetbrains.annotations.NotNull;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
import javax.imageio.ImageIO;
|
|
|
import java.awt.*;
|
|
|
import java.awt.image.BufferedImage;
|
|
|
-import java.io.File;
|
|
|
-import java.io.FileOutputStream;
|
|
|
-import java.io.IOException;
|
|
|
+import java.io.*;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.net.URL;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.MessageDigest;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+import java.time.Duration;
|
|
|
import java.util.List;
|
|
|
import java.util.*;
|
|
|
+import java.util.concurrent.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
@@ -67,6 +76,10 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
|
|
|
@Value("${server.tempDir}")
|
|
|
private String tempDir;
|
|
|
|
|
|
+ private static final String SOURCE_FILE_COMPRESS_STATUS = "global:compress:status:";
|
|
|
+
|
|
|
+ // 30分钟
|
|
|
+ private static final long TIMEOUT = 30 * 60 * 1000;
|
|
|
/**
|
|
|
* 查询文件资源
|
|
|
*
|
|
|
@@ -150,7 +163,7 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
|
|
|
* @return 是否新增成功
|
|
|
*/
|
|
|
@Override
|
|
|
- @Transactional(rollbackFor = Exception.class)
|
|
|
+ // @Transactional(rollbackFor = Exception.class)
|
|
|
public Boolean insertByBo(SmsbMinioDataBo bo) {
|
|
|
SmsbMinioData add = MapstructUtils.convert(bo, SmsbMinioData.class);
|
|
|
// 根据ossId 获取文件详细信息
|
|
|
@@ -162,69 +175,122 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
|
|
|
return false;
|
|
|
}
|
|
|
List<Long> sourceTreeIds = bo.getSourceTreeIds();
|
|
|
- List<SmsbMinioTransRecord> transRecordList = new ArrayList<>();
|
|
|
+ List<SmsbMinioData> minioDataList = new ArrayList<>();
|
|
|
for (String ossId : ossIdList) {
|
|
|
SysOssVo sysOssVo = sysOssMapper.selectVoById(ossId);
|
|
|
- // 创建add
|
|
|
- createSaveMinioData(sysOssVo, add);
|
|
|
- boolean flag = baseMapper.insert(add) > 0;
|
|
|
+ // 创建minioData
|
|
|
+ SmsbMinioData minioData = createSaveMinioData(sysOssVo);
|
|
|
+ boolean flag = baseMapper.insert(minioData) > 0;
|
|
|
if (flag) {
|
|
|
- bo.setId(add.getId());
|
|
|
+ bo.setId(minioData.getId());
|
|
|
// 保存sourceTree-source关联关系
|
|
|
if (CollectionUtil.isNotEmpty(sourceTreeIds)) {
|
|
|
sourceTreeIds.forEach(sourceTreeId -> {
|
|
|
SmsbSourceTreeRel sourceTreeRel = new SmsbSourceTreeRel();
|
|
|
- sourceTreeRel.setSourceId(add.getId());
|
|
|
+ sourceTreeRel.setSourceId(minioData.getId());
|
|
|
sourceTreeRel.setTreeId(sourceTreeId);
|
|
|
smsbSourceTreeRelMapper.insert(sourceTreeRel);
|
|
|
});
|
|
|
} else {
|
|
|
SmsbSourceTreeRel sourceTreeRel = new SmsbSourceTreeRel();
|
|
|
- sourceTreeRel.setSourceId(add.getId());
|
|
|
+ sourceTreeRel.setSourceId(minioData.getId());
|
|
|
sourceTreeRel.setTreeId(0L);
|
|
|
smsbSourceTreeRelMapper.insert(sourceTreeRel);
|
|
|
}
|
|
|
// 创建转码任务
|
|
|
- SmsbMinioTransRecord transRecord = createTransRecord(add);
|
|
|
- transRecordList.add(transRecord);
|
|
|
+ SmsbMinioTransRecord transRecord = createTransRecord(minioData);
|
|
|
+ minioDataList.add(minioData);
|
|
|
}
|
|
|
}
|
|
|
- doMinioFileTrans(transRecordList, baseMapper, transRecordMapper);
|
|
|
+ // updateFileTransResult(minioDataList, baseMapper, transRecordMapper);
|
|
|
+ CompletableFuture.runAsync(() -> updateFileTransResult(minioDataList, baseMapper, transRecordMapper));
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- private void doMinioFileTrans(List<SmsbMinioTransRecord> transRecordList, SmsbMinioDataMapper baseMapper, SmsbMinioTransRecordMapper transRecordMapper) {
|
|
|
+ private void updateFileTransResult(List<SmsbMinioData> minioDataList, SmsbMinioDataMapper baseMapper, SmsbMinioTransRecordMapper transRecordMapper) {
|
|
|
+ ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(1);
|
|
|
Runnable transTask = () -> {
|
|
|
- for (SmsbMinioTransRecord transRecord : transRecordList) {
|
|
|
- log.info("doMinioFileTrans transRecord : " + transRecord.toString());
|
|
|
- SmsbMinioData smsbMinioData = baseMapper.selectById(transRecord.getFileId());
|
|
|
- log.info("doMinioFileTrans smsbMinioData : " + smsbMinioData.toString());
|
|
|
- // 图片转码
|
|
|
- // 更新转码记录状态 正在转
|
|
|
- transRecord.setTransProgress(1);
|
|
|
- transRecordMapper.updateById(transRecord);
|
|
|
- if (transRecord.getTransType() == 1) {
|
|
|
- log.info("transTask start : " + smsbMinioData.getId() + ",file Type is image");
|
|
|
- compressImg(smsbMinioData, transRecord);
|
|
|
- } else if (transRecord.getTransType() == 2) {
|
|
|
- // TODO 视频转码
|
|
|
+ int cycleTimes = minioDataList.size();
|
|
|
+ while (true) {
|
|
|
+ if (cycleTimes <= 0) {
|
|
|
+ log.info("updateFileTransResult cycleTimes <= 0 break task is end");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ for (SmsbMinioData minioData : minioDataList) {
|
|
|
+ try {
|
|
|
+ // 等待5秒
|
|
|
+ Thread.sleep(5000);
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ String redisKey = SOURCE_FILE_COMPRESS_STATUS + minioData.getValue();
|
|
|
+ Integer transProgress = 0;
|
|
|
+ Integer transResult = 0;
|
|
|
+ String redisValue = RedisUtils.getCacheObject(redisKey);
|
|
|
+ if (StringUtils.isEmpty(redisValue)) {
|
|
|
+ log.info("redis key get value is empty,redisKey : " + redisKey);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if ("transing".equals(redisValue)) {
|
|
|
+ log.info("redis key get value is transing,redisKey : " + redisKey);
|
|
|
+ // 更新转码记录根据fileId状态
|
|
|
+ transProgress = 1;
|
|
|
+ transRecordMapper.updateByFileId(minioData.getId(),transProgress,transResult);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (redisValue.startsWith("success_")) {
|
|
|
+ log.info("redis key get value is success,redisKey : " + redisKey + ",redis value : " + redisValue);
|
|
|
+ String imageUrl = redisValue.replace("success_","");
|
|
|
+ transProgress = 2;
|
|
|
+ transResult = 1;
|
|
|
+ transRecordMapper.updateByFileId(minioData.getId(),transProgress,transResult);
|
|
|
+ minioData.setTransState(1);
|
|
|
+ minioData.setScreenshot(imageUrl);
|
|
|
+ baseMapper.updateById(minioData);
|
|
|
+ RedisUtils.deleteObject(redisKey);
|
|
|
+ cycleTimes--;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if ("fail".equals(redisValue)) {
|
|
|
+ log.info("redis key get value is fail,redisKey : " + redisKey);
|
|
|
+ transProgress = 2;
|
|
|
+ transResult = 2;
|
|
|
+ transRecordMapper.updateByFileId(minioData.getId(),transProgress,transResult);
|
|
|
+ minioData.setTransState(2);
|
|
|
+ baseMapper.updateById(minioData);
|
|
|
+ RedisUtils.deleteObject(redisKey);
|
|
|
+ cycleTimes--;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- // 更新转码记录状态
|
|
|
- baseMapper.updateById(smsbMinioData);
|
|
|
- transRecordMapper.updateById(transRecord);
|
|
|
- log.info("transTask end : " + smsbMinioData.getId() + ",trans result : " + smsbMinioData.getTransState());
|
|
|
- }
|
|
|
};
|
|
|
// 提交Runnable任务到线程池
|
|
|
- threadPoolTaskExecutor.submit(transTask);
|
|
|
+ Future<?> future = threadPoolTaskExecutor.submit(transTask);
|
|
|
+ timeoutExecutor.schedule(() -> {
|
|
|
+ if (!future.isDone()) {
|
|
|
+ log.info("updateFileTransResult Task is taking too long, terminating...");
|
|
|
+ // 强制终止任务
|
|
|
+ future.cancel(true);
|
|
|
+ }
|
|
|
+ }, TIMEOUT, TimeUnit.MILLISECONDS);
|
|
|
+ // 等待任务完成或超时
|
|
|
+ try {
|
|
|
+ future.get();
|
|
|
+ } catch (InterruptedException | ExecutionException e) {
|
|
|
+ log.error("updateFileTransResult Task execution failed", e);
|
|
|
+ } finally {
|
|
|
+ future.cancel(true);
|
|
|
+ timeoutExecutor.shutdown();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- public void compressImg(SmsbMinioData smsbMinioData, SmsbMinioTransRecord transRecord) {
|
|
|
+ public boolean compressImg(SysOssVo sysOssVo, MultipartFile file, String redisKey) {
|
|
|
+ RedisUtils.setCacheObject(redisKey, "transing", Duration.ofMinutes(30));
|
|
|
// 图片的横屏、竖屏
|
|
|
- String hwType = getHWType(smsbMinioData);
|
|
|
+ String hwType = getHWType(sysOssVo);
|
|
|
// 压缩前分辨率
|
|
|
- Integer width = Integer.parseInt(smsbMinioData.getResolution().split("x")[0]);
|
|
|
- Integer height = Integer.parseInt(smsbMinioData.getResolution().split("x")[1]);
|
|
|
+ Integer width = Integer.parseInt(sysOssVo.getResolution().split("x")[0]);
|
|
|
+ Integer height = Integer.parseInt(sysOssVo.getResolution().split("x")[1]);
|
|
|
// 缩放比率因子,保留2位小数
|
|
|
double factor = 0.0;
|
|
|
// 压缩后分辨率
|
|
|
@@ -243,25 +309,18 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
|
|
|
}
|
|
|
log.info("image hwType is : " + hwType + ",imageWidth : " + imageWidth + ",imageHeight : " + imageHeight);
|
|
|
// 图片压缩
|
|
|
- boolean compressResult = doCompressImg(imageWidth, imageHeight, smsbMinioData);
|
|
|
- // 压缩成功 更新主表和记录表状态
|
|
|
- if (compressResult) {
|
|
|
- smsbMinioData.setTransState(1);
|
|
|
- transRecord.setResult(1);
|
|
|
- } else {
|
|
|
- smsbMinioData.setTransState(2);
|
|
|
- transRecord.setResult(2);
|
|
|
- }
|
|
|
- transRecord.setTransProgress(2);
|
|
|
+ boolean compressResult = doCompressImg(imageWidth, imageHeight, redisKey, file);
|
|
|
+ return compressResult;
|
|
|
}
|
|
|
|
|
|
- private boolean doCompressImg(double imageWidth, double imageHeight, SmsbMinioData smsbMinioData) {
|
|
|
+ private boolean doCompressImg(double imageWidth, double imageHeight,String redisKey, MultipartFile file) {
|
|
|
// 临时文件
|
|
|
String tempFile = tempDir + File.separator + System.currentTimeMillis() + ".png";
|
|
|
+ File imageFile = null;
|
|
|
try {
|
|
|
- URL srcFileUrl = new URL(smsbMinioData.getFileUrl());
|
|
|
+ imageFile = convertMultipartFileToFile(file);
|
|
|
//获取图片对象
|
|
|
- Image src = javax.imageio.ImageIO.read(srcFileUrl);
|
|
|
+ Image src = javax.imageio.ImageIO.read(imageFile);
|
|
|
//缓存图片对象
|
|
|
BufferedImage to = new BufferedImage((int) imageWidth, (int) imageHeight,
|
|
|
BufferedImage.TYPE_INT_RGB);
|
|
|
@@ -279,15 +338,18 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
|
|
|
compressImageSize(tempFile);
|
|
|
// 上传文件至minio
|
|
|
String imageUrl = uploadCompressImage(tempFile);
|
|
|
- // 压缩成功,报错压缩图片路径
|
|
|
- smsbMinioData.setScreenshot(imageUrl);
|
|
|
+ RedisUtils.setCacheObject(redisKey, "success_" + imageUrl, Duration.ofMinutes(10));
|
|
|
return true;
|
|
|
} catch (IOException ex) {
|
|
|
log.error("doCompressImg exception : " + ex.getMessage());
|
|
|
+ RedisUtils.setCacheObject(redisKey, "fail", Duration.ofMinutes(10));
|
|
|
return false;
|
|
|
} finally {
|
|
|
// 文件删除
|
|
|
deleteFiles(tempFile);
|
|
|
+ if (imageFile != null) {
|
|
|
+ imageFile.delete();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -333,9 +395,9 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
|
|
|
}
|
|
|
|
|
|
|
|
|
- private String getHWType(SmsbMinioData smsbMinioData) {
|
|
|
- Integer width = Integer.parseInt(smsbMinioData.getResolution().split("x")[0]);
|
|
|
- Integer height = Integer.parseInt(smsbMinioData.getResolution().split("x")[1]);
|
|
|
+ private String getHWType(SysOssVo sysOssVo) {
|
|
|
+ Integer width = Integer.parseInt(sysOssVo.getResolution().split("x")[0]);
|
|
|
+ Integer height = Integer.parseInt(sysOssVo.getResolution().split("x")[1]);
|
|
|
if (width >= height) {
|
|
|
return "width";
|
|
|
} else {
|
|
|
@@ -355,7 +417,8 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
|
|
|
return transRecord;
|
|
|
}
|
|
|
|
|
|
- private void createSaveMinioData(SysOssVo sysOssVo, SmsbMinioData add) {
|
|
|
+ private SmsbMinioData createSaveMinioData(SysOssVo sysOssVo) {
|
|
|
+ SmsbMinioData add = new SmsbMinioData();
|
|
|
add.setId(null);
|
|
|
add.setDelFlag(0);
|
|
|
add.setKeyName(sysOssVo.getFileName());
|
|
|
@@ -374,6 +437,7 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
|
|
|
// validEntityBeforeSave(add);
|
|
|
add.setValue(sysOssVo.getOssId().toString());
|
|
|
add.setCreateUser(LoginHelper.getLoginUser().getUsername());
|
|
|
+ return add;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -474,4 +538,232 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
|
|
|
result.setFree(total - result.getUsed());
|
|
|
return result;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库
|
|
|
+ *
|
|
|
+ * @param file 要上传的 MultipartFile 对象
|
|
|
+ * @return 上传成功后的 SysOssVo 对象,包含文件信息
|
|
|
+ * @throws ServiceException 如果上传过程中发生异常,则抛出 ServiceException 异常
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public SysOssVo uploadSmsb(MultipartFile file) {
|
|
|
+ // 计算文件的MD5
|
|
|
+ String md5 = getFileMD5(file);
|
|
|
+ // 根据Md5查询当前租户下是否存在相同文件
|
|
|
+ SysOssVo sysOss = sysOssMapper.selectVoOne(new LambdaQueryWrapper<SysOss>().eq(SysOss::getFileMd5, md5));
|
|
|
+ if (sysOss != null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String originalfileName = file.getOriginalFilename();
|
|
|
+ String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
|
|
|
+ OssClient storage = OssFactory.instance();
|
|
|
+ UploadResult uploadResult;
|
|
|
+ try {
|
|
|
+ uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new ServiceException(e.getMessage());
|
|
|
+ }
|
|
|
+ // 保存文件信息
|
|
|
+ return buildResultEntityV2(originalfileName, suffix, storage.getConfigKey(), uploadResult, md5, file);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getFileMD5(MultipartFile file) {
|
|
|
+ try (InputStream inputStream = file.getInputStream()) {
|
|
|
+ MessageDigest md = MessageDigest.getInstance("MD5");
|
|
|
+ byte[] buffer = new byte[1024];
|
|
|
+ int bytesRead;
|
|
|
+
|
|
|
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
|
+ md.update(buffer, 0, bytesRead);
|
|
|
+ }
|
|
|
+
|
|
|
+ byte[] md5Bytes = md.digest();
|
|
|
+ StringBuilder hexString = new StringBuilder();
|
|
|
+ for (byte b : md5Bytes) {
|
|
|
+ hexString.append(String.format("%02x", b));
|
|
|
+ }
|
|
|
+ return hexString.toString();
|
|
|
+ } catch (IOException | NoSuchAlgorithmException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @NotNull
|
|
|
+ private SysOssVo buildResultEntityV2(String originalfileName, String suffix, String configKey, UploadResult uploadResult, String md5, MultipartFile file) {
|
|
|
+ SysOss oss = new SysOss();
|
|
|
+ oss.setUrl(uploadResult.getUrl());
|
|
|
+ oss.setFileSuffix(suffix);
|
|
|
+ oss.setFileName(uploadResult.getFilename());
|
|
|
+ oss.setOriginalName(originalfileName);
|
|
|
+ oss.setService(configKey);
|
|
|
+ oss.setFileMd5(md5);
|
|
|
+ getFileInfo(oss, file);
|
|
|
+ sysOssMapper.insert(oss);
|
|
|
+ SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
|
|
|
+ sysOssVo = matchingUrl(sysOssVo);
|
|
|
+ compressTask(sysOssVo, file);
|
|
|
+ return sysOssVo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void compressTask(SysOssVo sysOssVo, MultipartFile file) {
|
|
|
+ // 启动线程开启转码压缩
|
|
|
+ Runnable transTask = () -> {
|
|
|
+ log.info("doMinioFileTrans sysOssVo : " + sysOssVo.toString());
|
|
|
+ // 缓存ossId
|
|
|
+ String redisKey = SOURCE_FILE_COMPRESS_STATUS + sysOssVo.getOssId().toString();
|
|
|
+ RedisUtils.setCacheObject(redisKey, "waiting", Duration.ofMinutes(30));
|
|
|
+ boolean compressResult = false;
|
|
|
+ if (sysOssVo.getFileType() == 1) {
|
|
|
+ log.info("transTask start : " + sysOssVo.getOssId() + ",file Type is image");
|
|
|
+ compressResult = compressImg(sysOssVo, file, redisKey);
|
|
|
+ } else if (sysOssVo.getFileType() == 2) {
|
|
|
+ // TODO 视频转码
|
|
|
+ }
|
|
|
+ log.info("transTask end : " + sysOssVo.getOssId() + ",trans result : " + compressResult);
|
|
|
+
|
|
|
+ };
|
|
|
+ // 提交Runnable任务到线程池
|
|
|
+ threadPoolTaskExecutor.submit(transTask);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 桶类型为 private 的URL 修改为临时URL时长为120s
|
|
|
+ *
|
|
|
+ * @param oss OSS对象
|
|
|
+ * @return oss 匹配Url的OSS对象
|
|
|
+ */
|
|
|
+ private SysOssVo matchingUrl(SysOssVo oss) {
|
|
|
+ OssClient storage = OssFactory.instance(oss.getService());
|
|
|
+ // 仅修改桶类型为 private 的URL,临时URL时长为120s
|
|
|
+ if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
|
|
|
+ oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
|
|
|
+ }
|
|
|
+ return oss;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void getFileInfo(SysOss oss, MultipartFile file) {
|
|
|
+ // 文件大小
|
|
|
+ oss.setFileSize((int) (file.getSize() / 1024));
|
|
|
+ // 分辨率
|
|
|
+ String resolution = "0x0";
|
|
|
+ // 视频时长
|
|
|
+ Integer duration = 0;
|
|
|
+ // 视频码率
|
|
|
+ Integer codeRate = 0;
|
|
|
+ // 文件类型
|
|
|
+ Integer fileType = isImage(file) ? 1 : 2;
|
|
|
+ oss.setFileType(fileType);
|
|
|
+ if (fileType == 1) {
|
|
|
+ resolution = getImageResolution(file);
|
|
|
+ }
|
|
|
+ if (fileType == 2) {
|
|
|
+ String videoInfo = getVideoInfo(file);
|
|
|
+ if (StringUtils.isEmpty(videoInfo)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ resolution = videoInfo.split(",")[0].trim();
|
|
|
+ codeRate = Integer.parseInt(videoInfo.split(",")[1].trim()) / 1000;
|
|
|
+ duration = Integer.parseInt(videoInfo.split(",")[2].trim());
|
|
|
+ }
|
|
|
+ oss.setResolution(resolution);
|
|
|
+ oss.setCodeRate(codeRate);
|
|
|
+ oss.setDuration(duration);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取图片的分辨率(宽度和高度)
|
|
|
+ *
|
|
|
+ * @param file 图片文件(MultipartFile)
|
|
|
+ * @return 返回图片的宽度和高度,例如 "1920x1080",如果失败返回 null
|
|
|
+ */
|
|
|
+ public static String getImageResolution(MultipartFile file) {
|
|
|
+ try {
|
|
|
+ BufferedImage image = ImageIO.read(file.getInputStream());
|
|
|
+ if (image != null) {
|
|
|
+ int width = image.getWidth();
|
|
|
+ int height = image.getHeight();
|
|
|
+ // 返回格式:1920x1080
|
|
|
+ return width + "x" + height;
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ // 读取失败
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断文件是否是图片
|
|
|
+ *
|
|
|
+ * @param file 上传的文件
|
|
|
+ * @return true:是图片,false:不是
|
|
|
+ */
|
|
|
+ public static boolean isImage(MultipartFile file) {
|
|
|
+ String contentType = file.getContentType();
|
|
|
+ return contentType != null && contentType.startsWith("image/");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取视频的分辨率、码率和时长
|
|
|
+ *
|
|
|
+ * @param file MultipartFile 视频文件
|
|
|
+ * @return 返回视频信息,例如 "1920x1080, 1500kbps, 120.5s",如果失败返回 null。
|
|
|
+ */
|
|
|
+ public static String getVideoInfo(MultipartFile file) {
|
|
|
+ // 将 MultipartFile 转为临时文件
|
|
|
+ File tempFile = convertMultipartFileToFile(file);
|
|
|
+ if (tempFile == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ // 执行 ffprobe 命令
|
|
|
+ ProcessBuilder pb = new ProcessBuilder(
|
|
|
+ "ffprobe",
|
|
|
+ "-v", "error",
|
|
|
+ "-select_streams", "v:0",
|
|
|
+ "-show_entries", "stream=width,height,bit_rate,duration",
|
|
|
+ "-of", "csv=p=0",
|
|
|
+ tempFile.getAbsolutePath()
|
|
|
+ );
|
|
|
+ Process process = pb.start();
|
|
|
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
|
|
|
+ String videoInfo = reader.readLine();
|
|
|
+ process.waitFor();
|
|
|
+ tempFile.delete(); // 删除临时文件
|
|
|
+
|
|
|
+ if (videoInfo != null) {
|
|
|
+ // 解析返回数据
|
|
|
+ String[] values = videoInfo.split(",");
|
|
|
+ if (values.length >= 4) {
|
|
|
+ // 分辨率
|
|
|
+ String resolution = values[0] + "x" + values[1];
|
|
|
+ // 视频时长
|
|
|
+ // String duration = String.format("%.2f", Double.parseDouble(values[3])) + "s";
|
|
|
+ String duration = values[2].split("\\.")[0];
|
|
|
+ // 码率 (转换为 kbps)
|
|
|
+ String bitrate = values[3].split("\\.")[0];
|
|
|
+ return resolution + ", " + bitrate + ", " + duration;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * MultipartFile 转 File
|
|
|
+ */
|
|
|
+ private static File convertMultipartFileToFile(MultipartFile file) {
|
|
|
+ try {
|
|
|
+ File tempFile = File.createTempFile("temp_", ".tmp");
|
|
|
+ file.transferTo(tempFile);
|
|
|
+ return tempFile;
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
}
|