feat:upload and play video
This commit is contained in:
parent
5656b93b19
commit
3009ae206f
@ -118,6 +118,14 @@
|
|||||||
<artifactId>tika-core</artifactId> <!-- 文件客户端:文件类型的识别 -->
|
<artifactId>tika-core</artifactId> <!-- 文件客户端:文件类型的识别 -->
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/io.minio/minio -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.minio</groupId>
|
||||||
|
<artifactId>minio</artifactId>
|
||||||
|
<version>8.5.14</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.ensign.crm.module.infra.config;
|
||||||
|
|
||||||
|
import io.minio.MinioClient;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: minio配置
|
||||||
|
* @Date: 29/11/2024 9:57 am
|
||||||
|
* @Created: by ZZSLL
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "minio")
|
||||||
|
public class MinioConfig {
|
||||||
|
private String endpoint;
|
||||||
|
private String accessKey;
|
||||||
|
private String secretKey;
|
||||||
|
private String bucketName;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MinioClient minioClient() {
|
||||||
|
return MinioClient.builder()
|
||||||
|
.endpoint(endpoint)
|
||||||
|
.credentials(accessKey, secretKey)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
@ -100,4 +100,10 @@ public class FileController {
|
|||||||
return success(BeanUtils.toBean(pageResult, FileRespVO.class));
|
return success(BeanUtils.toBean(pageResult, FileRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/upload/minio")
|
||||||
|
@Operation(summary = "上传Minio")
|
||||||
|
public CommonResult<String> uploadMinio(MultipartFile file) {
|
||||||
|
return success(fileService.createMinioFile(file));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.ensign.crm.module.infra.controller.admin.video;
|
||||||
|
|
||||||
|
import com.ensign.crm.framework.common.pojo.CommonResult;
|
||||||
|
import com.ensign.crm.module.infra.service.file.FileService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.security.PermitAll;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: 播放minio中视频
|
||||||
|
* @Date: 29/11/2024 10:27 am
|
||||||
|
* @Created: by ZZSLL
|
||||||
|
*/
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/infra/video")
|
||||||
|
@Tag(name = "管理后台 - 视频")
|
||||||
|
public class VideoController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FileService fileService;
|
||||||
|
|
||||||
|
@GetMapping("/play")
|
||||||
|
@Operation(summary = "播放视频")
|
||||||
|
@PermitAll // 仅测试
|
||||||
|
public CommonResult<String> play(@RequestParam String bucket, @RequestParam String path) {
|
||||||
|
return CommonResult.success(fileService.readAsVideo(bucket, path));
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import com.ensign.crm.module.infra.controller.admin.file.vo.file.FileCreateReqVO
|
|||||||
import com.ensign.crm.module.infra.controller.admin.file.vo.file.FilePageReqVO;
|
import com.ensign.crm.module.infra.controller.admin.file.vo.file.FilePageReqVO;
|
||||||
import com.ensign.crm.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
|
import com.ensign.crm.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
|
||||||
import com.ensign.crm.module.infra.dal.dataobject.file.FileDO;
|
import com.ensign.crm.module.infra.dal.dataobject.file.FileDO;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件 Service 接口
|
* 文件 Service 接口
|
||||||
@ -63,4 +64,18 @@ public interface FileService {
|
|||||||
*/
|
*/
|
||||||
FilePresignedUrlRespVO getFilePresignedUrl(String path) throws Exception;
|
FilePresignedUrlRespVO getFilePresignedUrl(String path) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件至minio
|
||||||
|
* @return 上传后完整路径
|
||||||
|
*/
|
||||||
|
String createMinioFile(MultipartFile file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频播放
|
||||||
|
*
|
||||||
|
* @param bucket minio桶名
|
||||||
|
* @param path 路径
|
||||||
|
* @return stream
|
||||||
|
*/
|
||||||
|
String readAsVideo(String bucket, String path);
|
||||||
}
|
}
|
||||||
|
@ -5,16 +5,20 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import com.ensign.crm.framework.common.pojo.PageResult;
|
import com.ensign.crm.framework.common.pojo.PageResult;
|
||||||
import com.ensign.crm.framework.common.util.io.FileUtils;
|
import com.ensign.crm.framework.common.util.io.FileUtils;
|
||||||
import com.ensign.crm.framework.common.util.object.BeanUtils;
|
import com.ensign.crm.framework.common.util.object.BeanUtils;
|
||||||
import com.ensign.crm.module.infra.framework.file.core.client.FileClient;
|
|
||||||
import com.ensign.crm.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
|
|
||||||
import com.ensign.crm.module.infra.framework.file.core.utils.FileTypeUtils;
|
|
||||||
import com.ensign.crm.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
|
import com.ensign.crm.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
|
||||||
import com.ensign.crm.module.infra.controller.admin.file.vo.file.FilePageReqVO;
|
import com.ensign.crm.module.infra.controller.admin.file.vo.file.FilePageReqVO;
|
||||||
import com.ensign.crm.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
|
import com.ensign.crm.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
|
||||||
import com.ensign.crm.module.infra.dal.dataobject.file.FileDO;
|
import com.ensign.crm.module.infra.dal.dataobject.file.FileDO;
|
||||||
import com.ensign.crm.module.infra.dal.mysql.file.FileMapper;
|
import com.ensign.crm.module.infra.dal.mysql.file.FileMapper;
|
||||||
|
import com.ensign.crm.module.infra.framework.file.core.client.FileClient;
|
||||||
|
import com.ensign.crm.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
|
||||||
|
import com.ensign.crm.module.infra.framework.file.core.utils.FileTypeUtils;
|
||||||
|
import com.ensign.crm.module.infra.utils.MinioUtils;
|
||||||
|
import io.minio.MinioClient;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
@ -26,6 +30,7 @@ import static com.ensign.crm.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXIS
|
|||||||
*
|
*
|
||||||
* @author ENSIGN
|
* @author ENSIGN
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class FileServiceImpl implements FileService {
|
public class FileServiceImpl implements FileService {
|
||||||
|
|
||||||
@ -35,6 +40,12 @@ public class FileServiceImpl implements FileService {
|
|||||||
@Resource
|
@Resource
|
||||||
private FileMapper fileMapper;
|
private FileMapper fileMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MinioUtils minioUtils;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MinioClient minioClient;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<FileDO> getFilePage(FilePageReqVO pageReqVO) {
|
public PageResult<FileDO> getFilePage(FilePageReqVO pageReqVO) {
|
||||||
return fileMapper.selectPage(pageReqVO);
|
return fileMapper.selectPage(pageReqVO);
|
||||||
@ -114,4 +125,18 @@ public class FileServiceImpl implements FileService {
|
|||||||
object -> object.setConfigId(fileClient.getId()));
|
object -> object.setConfigId(fileClient.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createMinioFile(MultipartFile file) {
|
||||||
|
return minioUtils.upload(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String readAsVideo(String bucket, String path) {
|
||||||
|
try {
|
||||||
|
return minioUtils.preview(path);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,210 @@
|
|||||||
|
package com.ensign.crm.module.infra.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FastByteArrayOutputStream;
|
||||||
|
import com.ensign.crm.module.infra.config.MinioConfig;
|
||||||
|
import io.minio.*;
|
||||||
|
import io.minio.http.Method;
|
||||||
|
import io.minio.messages.Bucket;
|
||||||
|
import io.minio.messages.Item;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.ServletOutputStream;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: Minio工具
|
||||||
|
* @Date: 29/11/2024 9:58 am
|
||||||
|
* @Created: by ZZSLL
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class MinioUtils {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MinioConfig prop;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MinioClient minioClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看存储bucket是否存在
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public Boolean bucketExists(String bucketName) {
|
||||||
|
boolean found;
|
||||||
|
try {
|
||||||
|
found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建存储bucket
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
public Boolean makeBucket(String bucketName) {
|
||||||
|
try {
|
||||||
|
minioClient.makeBucket(MakeBucketArgs.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.build());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 删除存储bucket
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
public Boolean removeBucket(String bucketName) {
|
||||||
|
try {
|
||||||
|
minioClient.removeBucket(RemoveBucketArgs.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.build());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取全部bucket
|
||||||
|
*/
|
||||||
|
public List<Bucket> getAllBuckets() {
|
||||||
|
try {
|
||||||
|
return minioClient.listBuckets();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传
|
||||||
|
*
|
||||||
|
* @param file 文件
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
public String upload(MultipartFile file) {
|
||||||
|
String originalFilename = file.getOriginalFilename();
|
||||||
|
if (StringUtils.isBlank(originalFilename)){
|
||||||
|
throw new RuntimeException("文件名为空");
|
||||||
|
}
|
||||||
|
String dateFolder = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||||
|
String fileHash;
|
||||||
|
try {
|
||||||
|
fileHash = DigestUtils.md5Hex(file.getInputStream());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("计算文件哈希值失败", e);
|
||||||
|
}
|
||||||
|
String fileExtension = StringUtils.substringAfterLast(originalFilename, ".");
|
||||||
|
String objectName = String.format("/%s/%s.%s", dateFolder, fileHash, fileExtension);
|
||||||
|
try {
|
||||||
|
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(prop.getBucketName()).object(objectName)
|
||||||
|
.stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
|
||||||
|
minioClient.putObject(objectArgs);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
throw new RuntimeException("上传Minio失败");
|
||||||
|
}
|
||||||
|
return objectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预览图片
|
||||||
|
* @param fileName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String preview(String fileName){
|
||||||
|
// 查看文件地址
|
||||||
|
GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(prop.getBucketName()).object(fileName).method(Method.GET).build();
|
||||||
|
try {
|
||||||
|
return minioClient.getPresignedObjectUrl(build);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件下载
|
||||||
|
* @param fileName 文件名称
|
||||||
|
* @param res response
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
public void download(String fileName, HttpServletResponse res) {
|
||||||
|
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName())
|
||||||
|
.object(fileName).build();
|
||||||
|
try (GetObjectResponse response = minioClient.getObject(objectArgs)){
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int len;
|
||||||
|
try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()){
|
||||||
|
while ((len=response.read(buf))!=-1){
|
||||||
|
os.write(buf,0,len);
|
||||||
|
}
|
||||||
|
os.flush();
|
||||||
|
byte[] bytes = os.toByteArray();
|
||||||
|
res.setCharacterEncoding("utf-8");
|
||||||
|
res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
|
||||||
|
try (ServletOutputStream stream = res.getOutputStream()){
|
||||||
|
stream.write(bytes);
|
||||||
|
stream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看文件对象
|
||||||
|
* @return 存储bucket内文件对象信息
|
||||||
|
*/
|
||||||
|
public List<Item> listObjects() {
|
||||||
|
Iterable<Result<Item>> results = minioClient.listObjects(
|
||||||
|
ListObjectsArgs.builder().bucket(prop.getBucketName()).build());
|
||||||
|
List<Item> items = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
for (Result<Item> result : results) {
|
||||||
|
items.add(result.get());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param fileName
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public boolean remove(String fileName){
|
||||||
|
try {
|
||||||
|
minioClient.removeObject( RemoveObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build());
|
||||||
|
}catch (Exception e){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -249,3 +249,9 @@ justauth:
|
|||||||
prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE::
|
prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE::
|
||||||
timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟
|
timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟
|
||||||
|
|
||||||
|
minio:
|
||||||
|
endpoint: http://127.0.0.1:9000
|
||||||
|
bucketName: video
|
||||||
|
accessKey: qTgkym1JPFY53UGEiDP8
|
||||||
|
secretKey: swuXwUczZtLt08Qx6ffDzQEhQVEilGCfmcae7bLh
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ spring:
|
|||||||
servlet:
|
servlet:
|
||||||
# 文件上传相关配置项
|
# 文件上传相关配置项
|
||||||
multipart:
|
multipart:
|
||||||
max-file-size: 16MB # 单个文件大小
|
max-file-size: 5GB # 单个文件大小
|
||||||
max-request-size: 32MB # 设置总上传的文件大小
|
max-request-size: 1TB # 设置总上传的文件大小
|
||||||
|
|
||||||
# Jackson 配置项
|
# Jackson 配置项
|
||||||
jackson:
|
jackson:
|
||||||
|
Loading…
Reference in New Issue
Block a user