|
@@ -0,0 +1,407 @@
|
|
|
+package com.qmth.teachcloud.mark.utils;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.qmth.boot.core.fss.service.FileService;
|
|
|
+import com.qmth.boot.core.fss.store.FileStore;
|
|
|
+import com.qmth.teachcloud.common.config.DictionaryConfig;
|
|
|
+import com.qmth.teachcloud.common.contant.SystemConstant;
|
|
|
+import com.qmth.teachcloud.common.domain.FssDomain;
|
|
|
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
|
|
|
+import com.qmth.teachcloud.common.enums.LocalCatalogEnum;
|
|
|
+import com.qmth.teachcloud.common.enums.UploadFileEnum;
|
|
|
+import com.qmth.teachcloud.mark.bean.FilePathVo;
|
|
|
+import org.apache.commons.codec.digest.DigestUtils;
|
|
|
+import org.apache.commons.io.FileUtils;
|
|
|
+import org.apache.commons.io.FilenameUtils;
|
|
|
+import org.apache.commons.io.IOUtils;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.io.*;
|
|
|
+import java.time.Duration;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.StringJoiner;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 文件存储工具类
|
|
|
+ */
|
|
|
+@Component
|
|
|
+public class FileStoreUtils {
|
|
|
+ private final static Logger log = LoggerFactory.getLogger(FileStoreUtils.class);
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private FileService fileService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private DictionaryConfig dictionaryConfig;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上传文件到本地
|
|
|
+ *
|
|
|
+ * @param inputStream 流
|
|
|
+ * @param finalFile 最终文件
|
|
|
+ * @param catalogType 文件类型
|
|
|
+ * @throws Exception 异常
|
|
|
+ */
|
|
|
+ public void copyInputStreamToFile(InputStream inputStream, File finalFile, String md5, LocalCatalogEnum catalogType) throws Exception {
|
|
|
+ String dirName = finalFile.getPath().replaceAll("\\\\", SystemConstant.ORG_SPLIT);
|
|
|
+ this.localUpload(dirName, inputStream, md5, catalogType);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上传文件到本地
|
|
|
+ *
|
|
|
+ * @param dirName 上传到地址
|
|
|
+ * @param inputStream 流
|
|
|
+ * @param catalogType 文件
|
|
|
+ */
|
|
|
+ public void localUpload(String dirName, InputStream inputStream, String md5, LocalCatalogEnum catalogType) throws Exception {
|
|
|
+ log.info("ossUpload is come in");
|
|
|
+ String configPath = "";
|
|
|
+ switch (catalogType) {
|
|
|
+ case LOCAL_FILE:
|
|
|
+ configPath = dictionaryConfig.fssPublicDomain().getConfig();
|
|
|
+ break;
|
|
|
+ case LOCAL_PDF:
|
|
|
+ configPath = dictionaryConfig.fssPrivateDomain().getConfig();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ dirName = dirName.replaceAll(configPath, "");
|
|
|
+ fileService.getFileStore(catalogType.getType()).write(dirName, inputStream, md5);
|
|
|
+ log.info("dirName:{}", dirName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上传文件到本地
|
|
|
+ *
|
|
|
+ * @param dirName 上传到地址
|
|
|
+ * @param inputStream 流
|
|
|
+ * @param fssType 文件
|
|
|
+ */
|
|
|
+ public void localUpload(String dirName, InputStream inputStream, String md5, String fssType) throws Exception {
|
|
|
+ log.info("ossUpload is come in");
|
|
|
+ FssDomain fssDomain = getFssDomain(fssType);
|
|
|
+ dirName = dirName.replaceAll(fssDomain.getConfig(), "");
|
|
|
+ fileService.getFileStore(fssType).write(dirName, inputStream, md5);
|
|
|
+ log.info("dirName:{}", dirName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上传文件
|
|
|
+ *
|
|
|
+ * @param dirName 上传到地址
|
|
|
+ * @param inputStream 流
|
|
|
+ * @param fssType fileStore类型
|
|
|
+ */
|
|
|
+ public void ossUpload(String dirName, InputStream inputStream, String md5, String fssType) throws Exception {
|
|
|
+ log.info("ossUpload is come in");
|
|
|
+ fileService.getFileStore(fssType).write(dirName, inputStream, md5);
|
|
|
+ log.info("dirName:{}", dirName);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上传文件
|
|
|
+ *
|
|
|
+ * @param dirName 上传到地址
|
|
|
+ * @param file 文件
|
|
|
+ * @param type fileStore类型
|
|
|
+ */
|
|
|
+ public void ossUpload(String dirName, File file, String md5, String type) throws Exception {
|
|
|
+ log.info("ossUpload is come in");
|
|
|
+ fileService.getFileStore(type).write(dirName, new FileInputStream(file), md5);
|
|
|
+ log.info("dirName:{}", dirName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上传文件
|
|
|
+ *
|
|
|
+ * @param dirName 上传到地址
|
|
|
+ * @param content 文件
|
|
|
+ * @param type fileStore类型
|
|
|
+ */
|
|
|
+ public void ossUpload(String dirName, String content, String type) throws Exception {
|
|
|
+ log.info("ossUpload is come in");
|
|
|
+ fileService.getFileStore(type).write(dirName, new ByteArrayInputStream(content.getBytes()), DigestUtils.md5Hex(new ByteArrayInputStream(content.getBytes())));
|
|
|
+ log.info("dirName:{}", dirName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从文件存储上下载文件到本地
|
|
|
+ *
|
|
|
+ * @param dirName 文件地址
|
|
|
+ * @param localPath 本地路径
|
|
|
+ * @param type fileStore类型
|
|
|
+ * @throws Exception 异常
|
|
|
+ */
|
|
|
+ public File ossDownload(String dirName, String localPath, String type) throws Exception {
|
|
|
+ log.info("ossDownload is come in");
|
|
|
+ return this.saveLocal(fileService.getFileStore(type).read(dirName), localPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从文件存储上下载文件到本地
|
|
|
+ *
|
|
|
+ * @param dirName 文件地址
|
|
|
+ * @param localFile 本地路径
|
|
|
+ * @param type fileStore类型
|
|
|
+ * @throws Exception 异常
|
|
|
+ */
|
|
|
+ public File ossDownload(String dirName, File localFile, String type) throws Exception {
|
|
|
+ log.info("ossDownload is come in");
|
|
|
+ InputStream inputStream = fileService.getFileStore(type).read(dirName);
|
|
|
+ FileUtils.copyInputStreamToFile(inputStream, localFile);
|
|
|
+ return localFile;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从文件存储上下载文件到byte[]
|
|
|
+ *
|
|
|
+ * @param objectName 文件地址
|
|
|
+ * @param type fileStore类型
|
|
|
+ * @throws Exception 异常
|
|
|
+ */
|
|
|
+ public byte[] ossDownload(String objectName, String type) throws Exception {
|
|
|
+ log.info("oss Download is come in");
|
|
|
+ return IOUtils.toByteArray(fileService.getFileStore(type).read(objectName));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从文件存储上下载文件到InputStream
|
|
|
+ *
|
|
|
+ * @param objectName 文件地址
|
|
|
+ * @param type fileStore类型
|
|
|
+ * @throws Exception 异常
|
|
|
+ */
|
|
|
+ public InputStream ossDownloadIs(String objectName, String type) throws Exception {
|
|
|
+ log.info("oss Download is come in");
|
|
|
+ return fileService.getFileStore(type).read(objectName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取文件访问url
|
|
|
+ *
|
|
|
+ * @param objectPath 文件路径
|
|
|
+ * @param fssType 文件上传的类型
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public String getPrivateUrl(String objectPath, String fssType) {
|
|
|
+ String server;
|
|
|
+ FssDomain fssDomain = getFssDomain(fssType);
|
|
|
+ if ("public".equals(fssType)) {
|
|
|
+ server = fssDomain.getServer();
|
|
|
+ return server + SystemConstant.ORG_SPLIT + objectPath;
|
|
|
+ } else if ("private".equals(fssType)) {
|
|
|
+ Boolean oss = dictionaryConfig.sysDomain().isOss();
|
|
|
+ if (Objects.nonNull(oss) && oss) {
|
|
|
+ FileStore fileStore = fileService.getFileStore(fssType);
|
|
|
+ return fileStore.getPresignedUrl(objectPath, Duration.ofMinutes(5L));
|
|
|
+ } else {
|
|
|
+ return fssDomain.getServer() + SystemConstant.ORG_SPLIT + objectPath;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("文件存储store类型不存在");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据数据库文件路径判断文件上传类型
|
|
|
+ *
|
|
|
+ * @param path 路径
|
|
|
+ * @return 类型
|
|
|
+ */
|
|
|
+ public UploadFileEnum getUploadEnumByPath(String path) {
|
|
|
+ path = path.replaceAll("\\\\", SystemConstant.ORG_SPLIT);
|
|
|
+ String target = path.substring(0, path.indexOf('/'));
|
|
|
+ return UploadFileEnum.valueOf(target.toUpperCase());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 文件存在某本地路径
|
|
|
+ *
|
|
|
+ * @param inputStream 输入流
|
|
|
+ * @param dirPath 本地路径
|
|
|
+ * @return 存好的文件
|
|
|
+ * @throws IOException 异常
|
|
|
+ */
|
|
|
+ public File saveLocal(InputStream inputStream, String dirPath) throws IOException {
|
|
|
+ String fileName = dirPath.substring(dirPath.lastIndexOf(File.separator) + 1);
|
|
|
+ String parentPath = dirPath.substring(0, dirPath.lastIndexOf(File.separator)) + File.separator;
|
|
|
+ String name = fileName.substring(0, fileName.lastIndexOf('.'));
|
|
|
+ String suffix = fileName.substring(fileName.lastIndexOf('.'));
|
|
|
+
|
|
|
+ File desFile = buildSingleFileName(parentPath, name, suffix, 0);
|
|
|
+ if (!desFile.exists()) {
|
|
|
+ desFile.getParentFile().mkdirs(); //目标文件目录不存在的话需要创建目录
|
|
|
+ desFile.createNewFile();
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ FileUtils.copyInputStreamToFile(inputStream, desFile);
|
|
|
+ return desFile;
|
|
|
+ } finally {
|
|
|
+ if (inputStream != null) {
|
|
|
+ inputStream.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建唯一的文件名称
|
|
|
+ *
|
|
|
+ * @param path 文件路径
|
|
|
+ * @param fileName 文件名称
|
|
|
+ * @param suffix 文件后缀
|
|
|
+ * @param index 当前下标,初次为0
|
|
|
+ * @return 唯一的文件名称
|
|
|
+ */
|
|
|
+ public static File buildSingleFileName(String path, String fileName, String suffix, Integer index) {
|
|
|
+ File file;
|
|
|
+ //下标不等于0开始拼后缀
|
|
|
+ if (index != 0) {
|
|
|
+ file = new File(path + fileName + "(" + index + ")" + suffix);
|
|
|
+ } else {
|
|
|
+ file = new File(path + fileName + suffix);
|
|
|
+ }
|
|
|
+ //判断文件是否存在 文件不存在退出递归
|
|
|
+ if (file.isFile()) {
|
|
|
+ //每次递归给下标加1
|
|
|
+ file = buildSingleFileName(path, fileName, suffix, ++index);
|
|
|
+ }
|
|
|
+ return file;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String uploadFileByPattern(InputStream inputStream, String md5, UploadFileEnum uploadFileEnum, String path) {
|
|
|
+ try {
|
|
|
+ String fileMd5 = DigestUtils.md5Hex(inputStream);
|
|
|
+ if (!Objects.equals(fileMd5, md5)) {
|
|
|
+ throw ExceptionResultEnum.MD5_EQUALS_FALSE.exception();
|
|
|
+ }
|
|
|
+ FssDomain fssDomain = getFssDomain(uploadFileEnum.getFssType());
|
|
|
+ boolean oss = dictionaryConfig.sysDomain().isOss();
|
|
|
+ String fileName = getFssPathByPattern(oss, uploadFileEnum, path);
|
|
|
+ String type;
|
|
|
+ //上传至oss
|
|
|
+ if (oss || fssDomain.getConfig().startsWith(SystemConstant.START_PARENT)) {
|
|
|
+ this.ossUpload(fileName, inputStream, md5, uploadFileEnum.getFssType());
|
|
|
+ type = SystemConstant.OSS;
|
|
|
+ }
|
|
|
+ // 上传本地服务器
|
|
|
+ else {
|
|
|
+ this.localUpload(fileName, inputStream, md5, uploadFileEnum.getFssType());
|
|
|
+ type = SystemConstant.LOCAL;
|
|
|
+ }
|
|
|
+ return JSON.toJSONString(new FilePathVo(fileName, uploadFileEnum, type));
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("文件上传失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public String filePreview(String path) {
|
|
|
+ String url = null;
|
|
|
+ FilePathVo filePathVo = JSON.parseObject(path, FilePathVo.class);
|
|
|
+ String type = filePathVo.getType();
|
|
|
+ String filePath = filePathVo.getPath();
|
|
|
+ UploadFileEnum uploadFileEnum = UploadFileEnum.valueOf(filePathVo.getUploadType());
|
|
|
+ FssDomain fssDomain = getFssDomain(uploadFileEnum.getFssType());
|
|
|
+ if (Objects.equals(type, SystemConstant.LOCAL)) {
|
|
|
+ if (filePath.contains(fssDomain.getServer())) {
|
|
|
+ url = filePath.substring(filePath.indexOf(fssDomain.getServer()), filePath.length());
|
|
|
+ } else {
|
|
|
+ url = getPrivateUrl(filePath, uploadFileEnum.getFssType());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ url = getPrivateUrl(filePath, uploadFileEnum.getFssType());
|
|
|
+ }
|
|
|
+ url = url.replaceAll("\\\\", SystemConstant.ORG_SPLIT);
|
|
|
+ return url;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 文件上传存储类型(私有、公有)
|
|
|
+ *
|
|
|
+ * @param fssType 文件上传类型
|
|
|
+ */
|
|
|
+ private FssDomain getFssDomain(String fssType) {
|
|
|
+ if (StringUtils.isBlank(fssType)) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("文件类型异常");
|
|
|
+ }
|
|
|
+ String type = fssType.toLowerCase();
|
|
|
+ FssDomain fssDomain = null;
|
|
|
+ switch (type) {
|
|
|
+ case "private":
|
|
|
+ fssDomain = dictionaryConfig.fssPrivateDomain();
|
|
|
+ break;
|
|
|
+ case "public":
|
|
|
+ fssDomain = dictionaryConfig.fssPublicDomain();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (fssDomain == null) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("文件上传类型异常");
|
|
|
+ }
|
|
|
+ return fssDomain;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用模板生成文件名
|
|
|
+ *
|
|
|
+ * @param oss 是否上传oss
|
|
|
+ * @param uploadFileEnum 文件上传类型
|
|
|
+ * @param path 文件路径
|
|
|
+ */
|
|
|
+ private String getFssPathByPattern(boolean oss, UploadFileEnum uploadFileEnum, String path) {
|
|
|
+ FssDomain fssDomain = getFssDomain(uploadFileEnum.getFssType());
|
|
|
+ StringJoiner stringJoiner = new StringJoiner("");
|
|
|
+ if (!oss && StringUtils.isNotBlank(fssDomain.getConfig()) && !fssDomain.getConfig().startsWith(SystemConstant.START_PARENT)) {
|
|
|
+ stringJoiner.add(fssDomain.getConfig()).add(File.separator);
|
|
|
+ }
|
|
|
+ stringJoiner.add(path);
|
|
|
+ return escapeFileName(stringJoiner.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用日期创建目录
|
|
|
+ *
|
|
|
+ * @param oss 是否上传oss
|
|
|
+ * @param uploadFileEnum 文件上传类型
|
|
|
+ * @param fileName 原文件名称
|
|
|
+ * @param useRandomName 是否使用文件别名
|
|
|
+ */
|
|
|
+ public String getFssPathByDate(boolean oss, UploadFileEnum uploadFileEnum, String fileName, boolean useRandomName) {
|
|
|
+ FssDomain fssDomain = getFssDomain(uploadFileEnum.getFssType());
|
|
|
+ StringJoiner stringJoiner = new StringJoiner("");
|
|
|
+ if (!oss && StringUtils.isNotBlank(fssDomain.getConfig()) && !fssDomain.getConfig().startsWith(SystemConstant.START_PARENT)) {
|
|
|
+ stringJoiner.add(fssDomain.getConfig()).add(File.separator);
|
|
|
+ }
|
|
|
+ LocalDateTime nowTime = LocalDateTime.now();
|
|
|
+ stringJoiner.add(uploadFileEnum.getTitle()).add(File.separator)
|
|
|
+ .add(String.valueOf(nowTime.getYear())).add(File.separator)
|
|
|
+ .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getMonthValue())).add(File.separator)
|
|
|
+ .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getDayOfMonth())).add(File.separator);
|
|
|
+ if (useRandomName) {
|
|
|
+ stringJoiner.add(SystemConstant.getNanoId()).add(FilenameUtils.getExtension(fileName));
|
|
|
+ } else {
|
|
|
+ stringJoiner.add(fileName);
|
|
|
+ }
|
|
|
+ return escapeFileName(stringJoiner.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理路径中"\\"
|
|
|
+ *
|
|
|
+ * @param fileName 文件名称
|
|
|
+ */
|
|
|
+ private String escapeFileName(String fileName) {
|
|
|
+ if (StringUtils.isBlank(fileName)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return fileName.replaceAll("\\\\", SystemConstant.ORG_SPLIT);
|
|
|
+ }
|
|
|
+}
|