cjl
2023-09-26 346288bc38bdab442721d7609caed86d03b9d2d4
screen-api/src/main/java/com/moral/api/service/impl/FileTableServiceImpl.java
New file
@@ -0,0 +1,238 @@
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.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.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;
/**
 * <p>
 * 附件表 服务实现类
 * </p>
 *
 * @author moral
 * @since 2023-09-21
 */
@Service
@Slf4j
public class FileTableServiceImpl extends ServiceImpl<FileTableMapper, FileTable> 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<FileVo> upload(List<MultipartFile> files, Integer sysCode) {
        List<FileVo> 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(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<FileVo> list,int relationId,int fileModule) {
        List<FileTable> existsList = this.lambdaQuery().eq(FileTable::getRelationId, relationId)
                .eq(FileTable::getFileModule, fileModule)
                .eq(FileTable::getIsDel, YesOrNo.NO.value).list();
        // 新增附件
        if (!CollectionUtils.isEmpty(list)) {
            List<FileTable> 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<FileVo> list(int relationId, int fileModule) {
        List<FileVo> list = new ArrayList<>();
        List<FileTable> 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;
    }
}