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 fileList) { int successCount = 0; int fileNumber = fileList.size(); List 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 fileList) { int successCount = 0; int fileNumber = fileList.size(); List 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 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 fileKeys) { List 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); } } }