From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master
---
jnpf-file/jnpf-file-biz/src/main/java/jnpf/service/impl/FileServiceImpl.java | 457 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 457 insertions(+), 0 deletions(-)
diff --git a/jnpf-file/jnpf-file-biz/src/main/java/jnpf/service/impl/FileServiceImpl.java b/jnpf-file/jnpf-file-biz/src/main/java/jnpf/service/impl/FileServiceImpl.java
new file mode 100644
index 0000000..abf97eb
--- /dev/null
+++ b/jnpf-file/jnpf-file-biz/src/main/java/jnpf/service/impl/FileServiceImpl.java
@@ -0,0 +1,457 @@
+package jnpf.service.impl;
+
+import cn.hutool.core.net.URLEncodeUtil;
+import cn.hutool.core.util.URLUtil;
+import com.google.common.collect.ImmutableList;
+import jakarta.servlet.http.HttpServletResponse;
+import jnpf.base.UserInfo;
+import jnpf.config.ConfigValueUtil;
+import jnpf.constant.FileTypeConstant;
+import jnpf.constant.GlobalConst;
+import jnpf.constant.MsgCode;
+import jnpf.entity.FileParameter;
+import jnpf.exception.DataException;
+import jnpf.model.*;
+import jnpf.service.FileService;
+import jnpf.util.*;
+import lombok.Cleanup;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.dromara.x.file.storage.core.FileInfo;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.MediaTypeFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StreamUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class FileServiceImpl implements FileService {
+
+ public static final List<String> IMAGE_PATH = ImmutableList.of("/api/file/Image/");
+
+ private final List<String> whiteImageFolder = Arrays.asList(FileTypeConstant.USERAVATAR.toLowerCase(), FileTypeConstant.BIVISUALPATH.toLowerCase());
+
+
+ private final ConfigValueUtil configValueUtil;
+
+
+ @Override
+ public String getPath(String type) {
+ return FilePathUtil.getFilePath(type);
+ }
+
+ @Override
+ public String getLocalBasePath() {
+ return FileUploadUtils.getLocalBasePath();
+ }
+
+ @Override
+ public UploaderVO uploadFile(MergeChunkDto mergeChunkDto, MultipartFile file) {
+ String fileType = UpUtil.getFileType(file);
+ if (StringUtil.isEmpty(fileType)) {
+ fileType = mergeChunkDto.getFileType();
+ }
+ //楠岃瘉绫诲瀷
+ if (!OptimizeUtil.fileType(configValueUtil.getAllowUploadFileType(), fileType)) {
+ throw new DataException(MsgCode.FA017.get());
+ }
+ PathTypeModel pathTypeModel = new PathTypeModel();
+ pathTypeModel.setPathType(mergeChunkDto.getPathType());
+ pathTypeModel.setTimeFormat(mergeChunkDto.getTimeFormat());
+ pathTypeModel.setSortRule(mergeChunkDto.getSortRule());
+ pathTypeModel.setFolder(mergeChunkDto.getFolder());
+
+ if ("selfPath".equals(pathTypeModel.getPathType()) && StringUtil.isNotEmpty(pathTypeModel.getFolder())) {
+ String folder = pathTypeModel.getFolder();
+ folder = folder.replace("\\\\", "/");
+ //鏂囦欢澶瑰悕浠ュ瓧姣嶆垨鏁板瓧寮�澶达紝鐢卞瓧姣嶃�佹暟瀛椼�佷笅鍒掔嚎鍜岃繛瀛楃缁勬垚锛岄暱搴︿笉瓒呰繃100涓瓧绗�
+ String regex = "^[a-zA-Z0-9][a-zA-Z0-9_\\-\\\\/]{0,99}$";
+ if (!folder.matches(regex)) {
+ throw new DataException(MsgCode.FA038.get());
+ }
+ }
+
+ //瀹為檯鏂囦欢鍚�
+ String fileName = DateUtil.dateNow("yyyyMMdd") + "_" + RandomUtil.uuId() + "." + fileType;
+ //鏂囦欢涓婁紶璺緞
+ String type = mergeChunkDto.getType();
+
+ //鏂囦欢鑷畾涔夎矾寰勭浉瀵硅矾寰�
+ StringBuilder relativeFilePath = new StringBuilder();
+ if ("selfPath".equals(pathTypeModel.getPathType()) && pathTypeModel.getSortRule() != null) {
+ // 鎸夎矾寰勮鍒欓『搴忔瀯寤虹敓鎴愮洰褰�
+ String sortRule = pathTypeModel.getSortRule();
+ List<String> rules = null;
+ if (sortRule.contains("[")) {
+ rules = JsonUtil.getJsonToList(sortRule, String.class);
+ } else {
+ rules = Arrays.asList(pathTypeModel.getSortRule().split(","));
+ }
+ for (String rule : rules) {
+ // 鎸夌敤鎴峰瓨鍌�
+ if ("1".equals(rule)) {
+ UserInfo userInfo = UserProvider.getUser();
+ relativeFilePath.append(userInfo.getUserAccount()).append("/");
+ }
+ // 鎸夌収鏃堕棿鏍煎紡
+ else if (StringUtil.isNotEmpty(pathTypeModel.getTimeFormat()) && "2".equals(rule)) {
+ String timeFormat = pathTypeModel.getTimeFormat();
+ timeFormat = timeFormat.replace("YYYY", "yyyy");
+ timeFormat = timeFormat.replace("DD", "dd");
+ LocalDate currentDate = LocalDate.now();
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(timeFormat);
+ String currentDateStr = currentDate.format(formatter);
+ relativeFilePath.append(currentDateStr);
+ if (!currentDateStr.endsWith("/")) {
+ relativeFilePath.append("/");
+ }
+ }
+ // 鎸夎嚜瀹氫箟鐩綍
+ else if (StringUtil.isNotEmpty(pathTypeModel.getFolder()) && "3".equals(rule)) {
+ String folder = pathTypeModel.getFolder();
+ folder = folder.replace("\\\\", "/");
+ relativeFilePath.append(folder);
+ if (!folder.endsWith("/")) {
+ relativeFilePath.append("/");
+ }
+ }
+ }
+
+ if (StringUtil.isNotEmpty(relativeFilePath.toString())) {
+ relativeFilePath = new StringBuilder(StringUtil.replaceMoreStrToOneStr(relativeFilePath.toString(), "/"));
+ if (relativeFilePath.toString().startsWith("/")) {
+ relativeFilePath = new StringBuilder(relativeFilePath.substring(1));
+ }
+ fileName = relativeFilePath.toString().replace("/", ",") + fileName;
+ }
+ }
+
+
+ UploaderVO vo = UploaderVO.builder().fileSize(file.getSize()).fileExtension(fileType).build();
+ FileInfo fileInfo = FileUploadUtils.uploadFile(new FileParameter(type, fileName).setThumbnail(true), file);
+ fileName = fileInfo.getFilename();
+ String thFilename = fileInfo.getThFilename();
+ if (!StringUtil.isNotEmpty(thFilename)) {
+ //灏忓浘娌℃湁鍘嬬缉鐩存帴鐢ㄥ師鍥�
+ thFilename = fileName;
+ }
+ //鑷畾涔夋枃浠跺疄闄呮枃浠跺悕
+ if (StringUtil.isNotEmpty(relativeFilePath.toString())) {
+ fileName = relativeFilePath.toString().replace("/", ",") + fileName;
+ thFilename = relativeFilePath.toString().replace("/", ",") + thFilename;
+ }
+ vo.setName(fileName);
+
+ vo.setUrl(UploaderUtil.uploaderImg(IMAGE_PATH.get(0) + type + "/", fileName));
+ vo.setThumbUrl(UploaderUtil.uploaderImg(IMAGE_PATH.get(0) + type + "/", thFilename));
+
+ //涓婁紶鍒版案涓�
+ configValueUtil.getPreviewType();
+ return vo;
+ }
+
+ @Override
+ public ChunkRes checkChunk(Chunk chunk) {
+ String type = chunk.getExtension();
+ if (!OptimizeUtil.fileType(configValueUtil.getAllowUploadFileType(), type)) {
+ throw new DataException(MsgCode.FA017.get());
+ }
+ String identifier = chunk.getIdentifier();
+ String path = getPath(FileTypeConstant.TEMPORARY);
+ String filePath = XSSEscape.escapePath(path + identifier);
+ List<File> chunkFiles = FileUtil.getFile(new File(FileUploadUtils.getLocalBasePath() + filePath));
+ List<Integer> existsChunk = chunkFiles.stream().filter(f -> {
+ if (f.getName().endsWith(".tmp")) {
+ FileUtils.deleteQuietly(f);
+ return false;
+ } else {
+ return f.getName().startsWith(identifier);
+ }
+ }).map(f -> Integer.parseInt(f.getName().replace(chunk.getIdentifier().concat("-"), ""))).collect(Collectors.toList());
+ return ChunkRes.builder().merge(chunk.getTotalChunks().equals(existsChunk.size())).chunkNumbers(existsChunk).build();
+ }
+
+ @Override
+ public ChunkRes uploadChunk(Chunk chunk, MultipartFile file) {
+ String type = chunk.getExtension();
+ if (!OptimizeUtil.fileType(configValueUtil.getAllowUploadFileType(), type)) {
+ throw new DataException(MsgCode.FA017.get());
+ }
+ ChunkRes chunkRes = ChunkRes.builder().build();
+ chunkRes.setMerge(false);
+ File chunkFile = null;
+ File chunkTmpFile = null;
+ try {
+ String filePath = FileUploadUtils.getLocalBasePath() + getPath(FileTypeConstant.TEMPORARY);
+ Integer chunkNumber = chunk.getChunkNumber();
+ String identifier = XSSEscape.escapePath(chunk.getIdentifier());
+ String chunkTempPath = XSSEscape.escapePath(filePath + identifier);
+ File path = new File(chunkTempPath);
+ if (!path.exists()) {
+ path.mkdirs();
+ }
+ String chunkName = XSSEscape.escapePath(identifier.concat("-") + chunkNumber);
+ String chunkTmpName = XSSEscape.escapePath(chunkName.concat(".tmp"));
+ chunkFile = new File(chunkTempPath, chunkName);
+ chunkTmpFile = new File(chunkTempPath, chunkTmpName);
+ if (chunkFile.exists() && chunkFile.length() == chunk.getCurrentChunkSize()) {
+ log.info("璇ュ垎鍧楀凡缁忎笂浼狅細" + chunkFile.getName());
+ } else {
+ @Cleanup InputStream inputStream = file.getInputStream();
+ FileUtils.copyInputStreamToFile(inputStream, chunkTmpFile);
+ boolean b = chunkTmpFile.renameTo(chunkFile);
+ if (b){
+ log.info("鎴愬姛");
+ }else {
+ log.info("澶辫触");
+ }
+ }
+ int existsSize = (int) FileUtil.getFile(new File(chunkTempPath)).stream().filter(f ->
+ f.getName().startsWith(identifier) && !f.getName().endsWith(".tmp")
+ ).count();
+ chunkRes.setMerge(Objects.equals(existsSize, chunk.getTotalChunks()));
+ } catch (Exception e) {
+ try {
+ FileUtils.deleteQuietly(chunkTmpFile);
+ FileUtils.deleteQuietly(chunkFile);
+ } catch (Exception ee) {
+ e.printStackTrace();
+ }
+ log.info("涓婁紶寮傚父锛�" + e);
+ throw new DataException(MsgCode.FA033.get());
+ }
+ return chunkRes;
+ }
+
+ @Override
+ public UploaderVO mergeChunk(MergeChunkDto mergeChunkDto) {
+ String identifier = XSSEscape.escapePath(mergeChunkDto.getIdentifier());
+ String path = FileUploadUtils.getLocalBasePath() + getPath(FileTypeConstant.TEMPORARY);
+ String filePath = XSSEscape.escapePath(path + identifier);
+ String uuid = RandomUtil.uuId();
+ String partFile = XSSEscape.escapePath(path + uuid + "." + mergeChunkDto.getExtension());
+ UploaderVO vo = UploaderVO.builder().build();
+ try (FileOutputStream destTempfos = new FileOutputStream(partFile, true)){
+ List<File> mergeFileList = FileUtil.getFile(new File(filePath));
+ for (int i = 0; i < mergeFileList.size(); i++) {
+ String chunkName = identifier.concat("-") + (i + 1);
+ File files = new File(filePath, chunkName);
+ if (files.exists()) {
+ FileUtils.copyFile(files, destTempfos);
+ }
+ }
+ File partFiles = new File(partFile);
+ if (partFiles.exists()) {
+ MultipartFile multipartFile = FileUtil.createFileItem(partFiles);
+ vo = uploadFile(mergeChunkDto, multipartFile);
+ FileUtil.deleteTmp(multipartFile);
+ }
+ } catch (Exception e) {
+ log.error("鍚堝苟鍒嗙墖澶辫触: {}", e.getMessage());
+ throw new DataException(MsgCode.FA033.get());
+ } finally {
+ FileUtils.deleteQuietly(new File(filePath));
+ FileUtils.deleteQuietly(new File(partFile));
+ }
+ return vo;
+ }
+
+ @Override
+ public void downloadFile(String encryption, String downName) {
+ String fileNameAll = DesUtil.aesDecode(encryption);
+ if (!StringUtil.isEmpty(fileNameAll)) {
+ fileNameAll = fileNameAll.replace("\n", "");
+ String[] data = fileNameAll.split("#");
+ String cacheKEY = data.length > 0 ? data[0] : "";
+ String fileName = XSSEscape.escapePath(data.length > 1 ? data[1] : "");
+ String type = data.length > 2 ? data[2] : "";
+ Object ticketObj = TicketUtil.parseTicket(cacheKEY);
+ fileName = URLUtil.decode(fileName, GlobalConst.DEFAULT_CHARSET);
+ FileParameter fileParameter = new FileParameter(type, fileName);
+ //楠岃瘉缂撳瓨
+ if (ticketObj != null) {
+ //鏌愪簺鎵嬫満娴忚鍣ㄤ笅杞藉悗浼氭湁鎻愮ず绐楀彛, 浼氳闂袱娆°�佷笁娆′笅杞藉湴鍧�锛岃姹傚ご涓嶆槸app绫诲瀷
+ TicketUtil.updateTicket(cacheKEY, "1", 10L);
+ FileUploadUtils.downloadFile(fileParameter, inputStream ->
+ FileDownloadUtil.outFile(inputStream, downName)
+ );
+ if (FileTypeConstant.FILEZIPDOWNTEMPPATH.equals(type) ) { //鍒犻櫎鎵撳寘鐨勪复鏃舵枃浠讹紝閲婃斁瀛樺偍
+ FileUploadUtils.deleteFileByPathAndFileName(fileParameter);
+ }
+ } else {
+ if (FileTypeConstant.FILEZIPDOWNTEMPPATH.equals(type)) { //鍒犻櫎鎵撳寘鐨勪复鏃舵枃浠讹紝閲婃斁瀛樺偍
+ FileUploadUtils.deleteFileByPathAndFileName(fileParameter);
+ }
+ throw new DataException(MsgCode.FA039.get());
+ }
+ }
+ }
+
+ @Override
+ public boolean fileExists(String path, String fileName) {
+ return FileUploadUtils.exists(new FileParameter(path, fileName));
+ }
+
+ @Override
+ public String previewFile(PreviewParams previewParams) {
+ //璇诲彇鍏佽鏂囦欢棰勮绫诲瀷
+ String allowPreviewType = configValueUtil.getAllowPreviewFileType();
+ String[] fileType = allowPreviewType.split(",");
+
+ String fileName = XSSEscape.escape(previewParams.getFileName());
+
+ //鏂囦欢棰勮绫诲瀷妫�楠�
+ String docType = fileName.substring(fileName.lastIndexOf(".") + 1);
+ String s = Arrays.stream(fileType).filter(type -> type.equals(docType)).findFirst().orElse(null);
+
+ if (StringUtil.isEmpty(s)) {
+ throw new DataException(MsgCode.FA040.get());
+ }
+
+ //瑙f瀽鏂囦欢url 鑾峰彇绫诲瀷
+ String type = null;
+
+ String fileNameAll = previewParams.getFileDownloadUrl();
+ if (!StringUtil.isEmpty(fileNameAll)) {
+ String[] data = fileNameAll.split("/");
+ if (data.length > 4) {
+ type = data[4];
+ } else {
+ type = "";
+ }
+
+ }
+ String url;
+ //鏂囦欢棰勮绛栫暐
+ if ("yozo".equals(configValueUtil.getPreviewType())) {
+
+ url = "";
+ } else {
+ String downFileName = fileName;
+ if (downFileName.contains(",")) {
+ downFileName = downFileName.substring(downFileName.lastIndexOf(",") + 1);
+ }
+ url = configValueUtil.getApiDomain() + IMAGE_PATH.get(0) + type + "/" + fileName + "?fullfilename=" + downFileName + "&s=" + UserProvider.getUser().getSecurityKey();
+ //encode缂栫爜
+ String fileUrl = Base64.encodeBase64String(url.getBytes());
+ url = configValueUtil.getKkFileUrl() + "onlinePreview?url=" + fileUrl;
+ }
+ return url;
+ }
+
+ @Override
+ public void flushFile(String type, String fileName, String securityKey, boolean redirect) {
+ //椤甸潰鎾斁瑙嗛鐨勬儏鍐典笅鍙栨秷閲嶅畾鍚�
+ boolean isPlayVideo = StringUtil.isNotEmpty(ServletUtil.getHeader(HttpHeaders.RANGE));
+ //鐩綍鏍¢獙, 寮�鏀綰serAvatar銆丅iVisualPath
+ if (!whiteImageFolder.contains(type.toLowerCase())) {
+ if (StringUtil.isEmpty(securityKey)) {
+ throw new DataException(MsgCode.FA039.get());
+ }
+ String ticket = DesUtil.aesOrDecode(securityKey, false, true);
+ String token = TicketUtil.parseTicket(ticket);
+ if (token == null) {
+ throw new DataException(MsgCode.FA039.get());
+ }
+ UserInfo user = UserProvider.getUser(token);
+ if (user.getUserId() == null) {
+ TicketUtil.deleteTicket(ticket);
+ throw new DataException(MsgCode.FA039.get());
+ }
+ }
+ if (!isPlayVideo && StringUtil.isNotEmpty(securityKey) && redirect) {
+ String downName = fileName;
+ if (downName.contains(",")) {
+ downName = downName.substring(downName.lastIndexOf(",") + 1);
+ }
+ String urlEncodeFileName = URLEncodeUtil.encode(fileName, GlobalConst.DEFAULT_CHARSET);
+ String urlEncodeDownName = URLEncodeUtil.encode(downName, GlobalConst.DEFAULT_CHARSET);
+ String url = configValueUtil.getApiDomain() + UploaderUtil.uploaderFile(urlEncodeFileName + "#" + type) + "&name=" + urlEncodeDownName + "&fullfilename=" + urlEncodeDownName;
+ try {
+ ServletUtil.getResponse().sendRedirect(url);
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ } else {
+ FileUploadUtils.downloadFile(new FileParameter(type, fileName), inputStream -> {
+ if (isPlayVideo) {
+ FileInfo remoteFileInfo = FileUploadUtils.getRemoteFileInfo(new FileParameter(type, fileName));
+ streamVideo(inputStream, remoteFileInfo.getFilename(), remoteFileInfo.getSize());
+ } else {
+ FileDownloadUtil.flushFile(inputStream, fileName);
+ }
+ });
+ }
+ }
+
+ /**
+ * 瑙嗛鏂偣缁紶鏂规硶
+ *
+ * @param input
+ * @param fileName
+ * @param length
+ */
+ public void streamVideo(InputStream input, String fileName, long length) {
+ HttpServletResponse response = ServletUtil.getResponse();
+ String rangeHeader = ServletUtil.getHeader(HttpHeaders.RANGE);
+ final long CHUNK_SIZE = (long) 1024 * 1024; // 1MB
+ String range = rangeHeader.replace("bytes=", "");
+ String[] ranges = range.split("-");
+
+ long start = Long.parseLong(ranges[0]);
+ long end;
+
+ if (ranges.length > 1 && !ranges[1].isEmpty()) {
+ // 瀹㈡埛绔槑纭寚瀹氫簡缁撴潫浣嶇疆锛歜ytes=start-end
+ end = Long.parseLong(ranges[1]);
+ } else {
+ // 瀹㈡埛绔彧鎸囧畾浜嗗紑濮嬩綅缃細bytes=start-
+ end = start + CHUNK_SIZE - 1;
+ }
+ // 纭繚涓嶈秴鍑烘枃浠惰竟鐣�
+ end = Math.min(end, length - 1);
+
+ // 杈圭晫妫�鏌�
+ if (start >= length || end < start) {
+ response.setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
+ response.setHeader("Content-Range", "bytes */" + length);
+ return;
+ }
+ long contentLength = end - start + 1;
+
+ if (fileName == null) {
+ fileName = ".mp4";
+ }
+ // 璁剧疆鍝嶅簲澶�
+ response.setStatus(HttpStatus.PARTIAL_CONTENT.value());
+ response.setContentType(MediaTypeFactory.getMediaType(fileName).orElse(MediaType.ALL).toString());
+ response.setHeader("Content-Length", String.valueOf(contentLength));
+ response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + length);
+ response.setHeader("Accept-Ranges", "bytes");
+
+ try {
+ // 娉ㄦ剰锛歟nd + 1 鏄洜涓篶opyRange鐨勭粨鏉熶綅缃槸涓嶅寘鍚殑
+ StreamUtils.copyRange(input, response.getOutputStream(), start, end + 1);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+}
--
Gitblit v1.8.0