package com.monkeylessey.file.serivce.impl;
|
|
import com.aliyun.oss.model.AppendObjectRequest;
|
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
|
import com.monkeylessey.constant.RedisKeyPrefixConstants;
|
|
import com.monkeylessey.enums.general.FileUploadTypeEnum;
|
import com.monkeylessey.exception.ServiceException;
|
import com.monkeylessey.file.config.OssTemplate;
|
import com.monkeylessey.file.domain.entity.FileInfo;
|
import com.monkeylessey.file.domain.vo.FileVo;
|
import com.monkeylessey.file.domain.vo.STSVO;
|
import com.monkeylessey.file.mapper.FileMapper;
|
import com.monkeylessey.file.properties.OssProperties;
|
import com.monkeylessey.file.serivce.OssFileService;
|
import com.monkeylessey.file.utils.FileUtil;
|
import com.monkeylessey.response.Result;
|
import com.monkeylessey.framework.utils.RedisUtil;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.context.annotation.Primary;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.StringUtils;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import java.io.ByteArrayInputStream;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Objects;
|
|
|
/**
|
* @author 29443
|
* @version 1.0
|
* @date 2022/4/25
|
*/
|
@Primary
|
@Service
|
@RequiredArgsConstructor
|
public class OssFileServiceImpl implements OssFileService {
|
|
private final OssTemplate ossTemplate;
|
private final OssProperties ossProperties;
|
private final FileMapper fileMapper;
|
private final RedisUtil redisUtil;
|
|
/**
|
* 单文件上传,返回有过期时间的链接
|
*
|
* @param file
|
* @return
|
*/
|
@Override
|
public Result putObject(MultipartFile file) {
|
FileInfo fileInfo = FileUtil.getFileInfo(file);
|
try {
|
ossTemplate.putObject(file.getInputStream(), fileInfo);
|
fileMapper.insert(fileInfo);
|
} catch (IOException e) {
|
e.printStackTrace();
|
throw new ServiceException("上传异常,请重试");
|
}
|
String url = ossTemplate.getHasExpireUrl(fileInfo.getFileKey());
|
FileVo fileVo = new FileVo().setUrl(url).setFileKey(fileInfo.getFileKey());
|
return Result.ok("上传成功").data(fileVo);
|
}
|
|
/**
|
* 单文件上传,返回没有过期时间的链接
|
*
|
* @param file
|
* @return
|
*/
|
@Override
|
public Result putObjectNoExpire(MultipartFile file) {
|
FileInfo fileInfo = FileUtil.getFileInfo(file);
|
try {
|
ossTemplate.putObject(file.getInputStream(), fileInfo);
|
fileMapper.insert(fileInfo);
|
} catch (IOException e) {
|
e.printStackTrace();
|
throw new ServiceException("上传异常,请重试");
|
}
|
FileVo fileVo = new FileVo().setUrl(ossTemplate.getNoExpireUrl(fileInfo.getFileKey())).setFileKey(fileInfo.getFileKey()).setOriginalName(fileInfo.getOriginalFilename());
|
return Result.ok("上传成功").data(fileVo);
|
}
|
|
/**
|
* 多文件上传,返回有过期时间的链接
|
*
|
* @param fileList
|
* @return
|
*/
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Result putObjects(List<MultipartFile> fileList) {
|
int successCount = 0;
|
int fileNumber = fileList.size();
|
List<FileVo> files = new ArrayList();
|
for (MultipartFile file : fileList) {
|
FileInfo fileInfo = FileUtil.getFileInfo(file);
|
try {
|
ossTemplate.putObject(file.getInputStream(), fileInfo);
|
fileMapper.insert(fileInfo);
|
files.add(new FileVo().setUrl(ossTemplate.getHasExpireUrl(fileInfo.getFileKey())).setFileKey(fileInfo.getFileKey()).setOriginalName(fileInfo.getOriginalFilename()));
|
successCount++;
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
}
|
String msg = String.format("上传文件总数:'{}',成功'{}'个,失败'{}'个",
|
fileNumber,
|
successCount,
|
fileNumber - successCount);
|
return Result.ok(msg).data(files);
|
}
|
|
/**
|
* 多文件上传,返回没有过期时间的链接
|
*
|
* @param fileList
|
* @return
|
*/
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Result putObjectsNoExpire(List<MultipartFile> fileList) {
|
int successCount = 0;
|
int fileNumber = fileList.size();
|
List<FileVo> files = new ArrayList();
|
for (MultipartFile file : fileList) {
|
FileInfo fileInfo = (FileInfo) FileUtil.getFileInfo(file);
|
try {
|
ossTemplate.putObject(file.getInputStream(), fileInfo);
|
fileMapper.insert(fileInfo);
|
files.add(new FileVo().setFileKey(fileInfo.getFileKey()).setUrl(ossTemplate.getNoExpireUrl(fileInfo.getFileKey())).setOriginalName(fileInfo.getOriginalFilename()));
|
successCount++;
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
}
|
String msg = String.format("上传文件总数:'{}',成功'{}'个,失败'{}'个",
|
fileNumber,
|
successCount,
|
fileNumber - successCount);
|
return Result.ok(msg).data(files);
|
}
|
|
/**
|
* 删除文件
|
*
|
* @param fileKey
|
* @return
|
*/
|
@Override
|
public Result deleteObject(String fileKey) {
|
ossTemplate.removeObject(fileKey);
|
new LambdaUpdateChainWrapper<>(fileMapper).eq(FileInfo::getFileKey, fileKey).remove();
|
return Result.ok("删除成功");
|
}
|
|
/**
|
* 批量删除文件
|
*
|
* @param fileKeys
|
* @return
|
*/
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Result deleteObjects(List<String> fileKeys) {
|
fileKeys.forEach(fileKey -> {
|
ossTemplate.removeObject(fileKey);
|
new LambdaUpdateChainWrapper<>(fileMapper).eq(FileInfo::getFileKey, fileKey).remove();
|
});
|
return Result.ok("批量删除成功");
|
}
|
|
/**
|
* 下载文件
|
*
|
* @param fileKey
|
* @return
|
*/
|
@Override
|
public Result getObject(String fileKey) {
|
InputStream object = ossTemplate.getObject(fileKey);
|
return Result.ok("下载成功").data(object);
|
}
|
|
/**
|
* 批量下载文件
|
*
|
* @param fileKeys
|
* @return
|
*/
|
@Override
|
public Result getObjects(List<String> fileKeys) {
|
List<InputStream> files = new ArrayList<>();
|
fileKeys.forEach(fileKey -> {
|
InputStream object = ossTemplate.getObject(fileKey);
|
files.add(object);
|
});
|
return Result.ok("下载成功").data(files);
|
}
|
|
|
/**
|
* 获取STS凭证
|
*
|
* @return
|
*/
|
@Override
|
public Result getSTSToken() {
|
String stsToken = redisUtil.getValue(RedisKeyPrefixConstants.OSSTOKEN, String.class);
|
String accessId = redisUtil.getValue(RedisKeyPrefixConstants.OSSACCESSKEYID, String.class);
|
String accessSecret = redisUtil.getValue(RedisKeyPrefixConstants.OSSACCESSKEYSECRET, String.class);
|
if (!StringUtils.hasText(stsToken) || !StringUtils.hasText(accessId) || !StringUtils.hasText(accessSecret)) {
|
ossTemplate.getSTSToken();
|
}
|
stsToken = redisUtil.getValue(RedisKeyPrefixConstants.OSSTOKEN, String.class);
|
accessId = redisUtil.getValue(RedisKeyPrefixConstants.OSSACCESSKEYID, String.class);
|
accessSecret = redisUtil.getValue(RedisKeyPrefixConstants.OSSACCESSKEYSECRET, String.class);
|
STSVO stsvo = new STSVO()
|
.setStsToken(stsToken)
|
.setAccessKeyId(accessId)
|
.setAccessKeySecret(accessSecret)
|
.setRegion(handleRegion(ossProperties.getEndpoint()))
|
.setBucket(ossProperties.getBucketName());
|
return Result.ok().data(stsvo);
|
}
|
|
/**
|
* 追加文件演示,追加上传适用于文件固定,即filekey固定的文件
|
* 如果是追加到动态(即用户上传一个文件,系统进行追加这种)
|
* 因为单从文件的属性上不能获取到一个不变固定的filekey,文件也就失去了唯一标识,不能根据filekey去数据库查询到该文件的追加位置
|
*/
|
@Override
|
public void uploadAppendDemo() {
|
// 追加的内容
|
String content0 = "Hello OSS -1 \n";
|
String content1 = "Hello OSS A \n";
|
String content2 = "Hello OSS B \n";
|
String content3 = "Hello OSS C \n";
|
String content4 = "Hello Append \n";
|
|
// 对于要追加的文件,先去数据库查找。
|
FileInfo fileInfo = new LambdaQueryChainWrapper<>(fileMapper)
|
.eq(FileInfo::getFileKey, "text/demo.txt")
|
.one();
|
// 数据库没有,说明是第一次上传,需要构建文件内容对象然后保存。第一次上传的逻辑也包含在内
|
if (Objects.isNull(fileInfo)) {
|
// 文件信息
|
fileInfo = new FileInfo();
|
fileInfo.setFileKey("text/demo.txt");
|
fileInfo.setOriginalFilename("abc.txt");
|
fileInfo.setUploadType(FileUploadTypeEnum.APPEND);
|
|
// 第一次上传
|
AppendObjectRequest appendObjectRequest = ossTemplate.firstAppend(new ByteArrayInputStream(content0.getBytes()), fileInfo, null);
|
// 后续追加
|
// 第一次追加(也可以把第一次上传理解为一次追加)
|
appendObjectRequest.setInputStream(new ByteArrayInputStream(content1.getBytes()));
|
appendObjectRequest = ossTemplate.nextAppend(appendObjectRequest);
|
|
// 第二次追加
|
appendObjectRequest.setInputStream(new ByteArrayInputStream(content2.getBytes()));
|
appendObjectRequest = ossTemplate.nextAppend(appendObjectRequest);
|
|
// 第三次追加
|
appendObjectRequest.setInputStream(new ByteArrayInputStream(content3.getBytes()));
|
appendObjectRequest = ossTemplate.nextAppend(appendObjectRequest);
|
// 追加完需要在数据库保存下次追加位置(必须持久化该值)
|
fileInfo.setAppendPosition(appendObjectRequest.getPosition());
|
}
|
// 数据库能找到说明,以前追加过,现在继续追加,追加位置拿数据库里查到的
|
else {
|
AppendObjectRequest appendObjectRequest = ossTemplate.getAppendObjectRequest(new ByteArrayInputStream(content4.getBytes()), fileInfo, ossTemplate.getDefaultMetadata(fileInfo));
|
appendObjectRequest.setPosition(fileInfo.getAppendPosition());
|
appendObjectRequest = ossTemplate.nextAppend(appendObjectRequest);
|
fileInfo.setAppendPosition(appendObjectRequest.getPosition());
|
}
|
this.saveOrUpdate(fileInfo);
|
}
|
|
|
/**
|
* 去掉region后面的.aliyuncs.com
|
*
|
* @param region
|
* @return
|
*/
|
public String handleRegion(String region) {
|
if (StringUtils.hasText(region)) {
|
int i = region.indexOf(".", 1);
|
return region.substring(0, i);
|
}
|
return null;
|
}
|
|
/**
|
* 添加或保存文件信息
|
* @param fileInfo
|
*/
|
private void saveOrUpdate(FileInfo fileInfo) {
|
if (StringUtils.hasText(fileInfo.getId())) {
|
fileMapper.updateById(fileInfo);
|
} else {
|
fileMapper.insert(fileInfo);
|
}
|
}
|
|
}
|