package com.moral.api.service.impl; 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; } }