package com.moral.api.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.moral.api.config.Interceptor.UserHelper;
import com.moral.api.entity.FileTable;
import com.moral.api.exception.BusinessException;
import com.moral.api.mapper.FileTableMapper;
import com.moral.api.pojo.enums.FileType;
import com.moral.api.pojo.enums.YesOrNo;
import com.moral.api.pojo.vo.file.FileAddressVo;
import com.moral.api.pojo.vo.file.FileVo;
import com.moral.api.pojo.vo.user.QxUser;
import com.moral.api.service.FileTableService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.moral.api.utils.FileTypeUtils;
import com.moral.api.utils.StringUtils;
import com.moral.util.DateUtils;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
/**
*
* 附件表 服务实现类
*
*
* @author moral
* @since 2023-09-21
*/
@Service
@Slf4j
public class FileTableServiceImpl extends ServiceImpl implements FileTableService {
@Value("${file.path}")
private String basePath;
@Override
public FileVo upload(MultipartFile file, Integer sysCode) {
String originalFilename = file.getOriginalFilename().replaceAll(StringUtils.SPACE, StringUtils.EMPTY).toLowerCase();
String suffix = originalFilename.substring(originalFilename.lastIndexOf(StringUtils.DOT));
FileType fileType = FileTypeUtils.getFileType(suffix.toLowerCase());
// 处理文件相对路径 系统代码 + 业务代码 + 日期
String filePath = getFilePath(sysCode.toString());
// 保存文件
String targetFolder = basePath.replaceAll(StringUtils.BACKSLASH.concat(StringUtils.BACKSLASH), StringUtils.SLASH).concat(StringUtils.SLASH).concat(filePath);
//使用uuid 替换上传文件名,避免重复
String storageFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf(StringUtils.DOT));
storageFile(file, storageFileName, targetFolder);
// 封装文件信息
QxUser user = UserHelper.getCurrentUser();
FileTable sysFile = new FileTable();
sysFile.setFileName(originalFilename).setFileType(fileType.getValue()).setFileSize((int) file.getSize()).setFileAddress(filePath.concat(storageFileName))
.setFileModule(sysCode).setCreateTime(new Date());
if (Objects.nonNull(user)) {
sysFile.setCreateId(user.getUserId()).setCreateName(user.getUserName());
}
save(sysFile);
return new FileVo().setFileId(sysFile.getFileId()).setFileName(originalFilename).setFileType(fileType.getValue());
}
@Override
public List upload(List files, Integer sysCode) {
List fileList = new LinkedList<>();
for (MultipartFile file : files) {
fileList.add(upload(file, sysCode));
}
return fileList;
}
@Override
public void preview(Integer id, HttpServletRequest request, HttpServletResponse response) {
FileTable sysFile = getById(id);
File file = new File(handleFileRealPath(sysFile.getFileAddress()));
String suffix = file.getName().substring(file.getName().lastIndexOf(StringUtils.DOT));
if (FileTypeUtils.SUFFIX_MP4.equals(suffix)) {
videoPreview(file, request, response);
} else {
try (OutputStream out = response.getOutputStream(); InputStream in = new FileInputStream(file)) {
response.setContentType(FileTypeUtils.convertHeaderType(suffix));
response.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(sysFile.getFileName(), "UTF-8"));
int len;
// 创建数据缓冲区
byte[] buffer = new byte[1024];
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} catch (Exception e) {
log.error("读取文件异常!!! e={}", e.getMessage());
}
}
}
private String getFilePath(String sysCode) {
return sysCode.concat(StringUtils.SLASH).concat(DateUtils.dateToDateString(new Date(),DateUtils.yyyyMMdd_EN)).concat(StringUtils.SLASH);
}
public void storageFile(MultipartFile file, String originalFilename, String targetFolder) {
File f = new File(targetFolder, originalFilename);
try {
// 保存文件
if (!f.getParentFile().exists()) {
if (!f.getParentFile().mkdirs()) {
log.error("error:创建文件失败! dir={}", f.getAbsolutePath());
}
}
file.transferTo(f);
} catch (IOException e) {
log.error("error:文件上传失败!!!e={}", e.getMessage());
throw new BusinessException("文件上传失败,请重试!");
}
}
private String handleFileRealPath(String path) {
return basePath.replaceAll(StringUtils.BACKSLASH.concat(StringUtils.BACKSLASH), StringUtils.SLASH).concat(StringUtils.SLASH).concat(path);
}
private void videoPreview(File file, HttpServletRequest request, HttpServletResponse response) {
String suffix = file.getName().substring(file.getName().lastIndexOf(StringUtils.DOT));
//设置属性
response.setContentType(FileTypeUtils.convertHeaderType(suffix));
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("ETag", file.getName());
response.setHeader("Last-Modified", new Date().toString());
long contentLength = file.length();
//每次传递实际长度(初始第一次读取少选数据)
long length = Math.min(contentLength, 1024);
long start = 0, end = 0;
String range = request.getHeader("Range");
//第一次请求只返回content length来让客户端请求多次实际数据
if (range == null) {
response.setHeader("Content-length", contentLength + "");
} else {
//以后的多次以断点续传的方式来返回视频数据
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);//206
if (range.startsWith("bytes=")) {
String[] values = range.split("=")[1].split("-");
start = Integer.parseInt(values[0]);
if (values.length > 1) {
end = Integer.parseInt(values[1]);
}
}
//无end,每次允许的最大长度
long maxSize = 1024 * 1024; //1MB
if (end > 0) {
length = end - start + 1;
response.setHeader("Content-length", "" + length);
response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + contentLength);
} else {
length = Math.min((contentLength - start), maxSize);
response.setHeader("Content-length", "" + length);
response.setHeader("Content-Range", "bytes " + start + "-" + (start + length - 1) + "/" + contentLength);
}
}
try {
FileInputStream fileInputStream = new FileInputStream(file);
OutputStream out = response.getOutputStream();
//将数据截取并存入byte[]数组中
byte[] data = new byte[(int) length];
fileInputStream.skip(start);
fileInputStream.read(data);
//取出要传给前端的有效数据
out.write(data);
out.close();
fileInputStream.close();
} catch (Exception e) {
log.error("读取文件异常!!! e={}", e.getMessage());
}
}
@Override
public void coverPreview(Integer id, HttpServletRequest request, HttpServletResponse response) {
FileTable sysFile = getById(id);
File file = new File(handleFileRealPath(sysFile.getFileAddress()));
String suffix = file.getName().substring(file.getName().lastIndexOf(StringUtils.DOT));
try (OutputStream out = response.getOutputStream()) {
response.setContentType(FileTypeUtils.convertHeaderType(suffix));
response.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(sysFile.getFileName(), "UTF-8"));
Thumbnails.of(file).size(420, 200).toOutputStream(out);
} catch (Exception e) {
log.error("读取文件异常!!! e={}", e.getMessage());
}
}
@Override
@Transactional
public void upDateResult(List list,int relationId,int fileModule) {
List existsList = this.lambdaQuery().eq(FileTable::getRelationId, relationId)
.eq(FileTable::getFileModule, fileModule)
.eq(FileTable::getIsDel, YesOrNo.NO.value).list();
// 新增附件
if (!CollectionUtils.isEmpty(list)) {
List addList = new ArrayList();
// 2.2.筛选出需要保存的文件
list.forEach(it -> {
existsList.removeIf(file -> file.getFileId().equals(it.getFileId()));
FileTable file = new FileTable();
BeanUtils.copyProperties(it,file);
file.setRelationId(relationId);
addList.add(file);
});
if (!CollectionUtils.isEmpty(addList)) {
// 保存
this.updateBatchById(addList);
}
}
// 删除附件
if (!CollectionUtils.isEmpty(existsList)) {
removeByIds(existsList.stream().map(FileTable::getFileId).collect(Collectors.toList()));
}
}
@Override
public List list(int relationId, int fileModule) {
List list = new ArrayList<>();
List existsList = this.lambdaQuery().eq(FileTable::getRelationId, relationId)
.eq(FileTable::getFileModule, fileModule)
.eq(FileTable::getIsDel, YesOrNo.NO.value).orderByAsc(FileTable::getCreateTime).list();
existsList.forEach(it->{
FileVo fileVo = new FileVo();
fileVo.setFileId(it.getFileId());
fileVo.setFileName(it.getFileName());
fileVo.setFileType(it.getFileType());
list.add(fileVo);
});
return list;
}
@Override
public List list(int relationId, int fileModule, int fileType) {
List list = new ArrayList<>();
List existsList = this.lambdaQuery().eq(FileTable::getRelationId, relationId)
.eq(FileTable::getFileModule, fileModule)
.eq(Objects.nonNull(fileType),FileTable::getFileType,fileType)
.eq(FileTable::getIsDel, YesOrNo.NO.value).orderByAsc(FileTable::getCreateTime).list();
existsList.forEach(it->{
FileAddressVo fileVo = new FileAddressVo();
fileVo.setFileId(it.getFileId());
fileVo.setFileName(it.getFileName());
fileVo.setFileType(it.getFileType());
fileVo.setFileAddress(it.getFileAddress());
list.add(fileVo);
});
return list;
}
}