package jnpf.service.impl; import cn.hutool.core.util.URLUtil; import jnpf.base.UserInfo; import jnpf.config.ConfigValueUtil; import jnpf.constant.FileTypeConstant; import jnpf.constant.GlobalConst; import jnpf.constant.MsgCode; import jnpf.consts.DeviceType; import jnpf.entity.FileParameter; import jnpf.exception.DataException; import jnpf.model.*; import jnpf.service.FileService; import jnpf.util.*; import lombok.Cleanup; 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.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; 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.*; import java.util.stream.Collectors; @Slf4j @Service public class FileServiceImpl implements FileService { private List whiteImageFolder = Arrays.asList(FileTypeConstant.USERAVATAR.toLowerCase(), FileTypeConstant.BIVISUALPATH.toLowerCase()); @Autowired private ConfigValueUtil configValueUtil; // @Autowired // private YozoUtils yozoUtils; @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()); // String orgFileName = file.getOriginalFilename(); // if (OptimizeUtil.fileSize(file.getSize(), 1024000)) { // return ActionResult.fail("上传失败,文件大小超过1M"); // } // if ("mail".equals(type)) { // type = "temporary"; // } if ("selfPath".equals(pathTypeModel.getPathType())) { if (StringUtil.isNotEmpty(pathTypeModel.getFolder())) { String folder = pathTypeModel.getFolder(); folder = folder.replaceAll("\\\\", "/"); // String regex = "^[a-z0-9A-Z\\u4e00-\\u9fa5\\\\\\/]+$"; //文件夹名以字母或数字开头,由字母、数字、下划线和连字符组成,长度不超过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 filePath = FilePathUtil.getFilePath(type.toLowerCase()); String type = mergeChunkDto.getType(); //文件自定义路径相对路径 String relativeFilePath = ""; if (pathTypeModel != null && "selfPath".equals(pathTypeModel.getPathType()) && pathTypeModel.getSortRule() != null) { // 按路径规则顺序构建生成目录 String sortRule = pathTypeModel.getSortRule(); List 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 += userInfo.getUserAccount() + "/"; } // 按照时间格式 else if (StringUtil.isNotEmpty(pathTypeModel.getTimeFormat()) && "2".equals(rule)) { String timeFormat = pathTypeModel.getTimeFormat(); timeFormat = timeFormat.replaceAll("YYYY", "yyyy"); timeFormat = timeFormat.replaceAll("DD", "dd"); LocalDate currentDate = LocalDate.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(timeFormat); String currentDateStr = currentDate.format(formatter); relativeFilePath += currentDateStr; if (!currentDateStr.endsWith("/")) { relativeFilePath += "/"; } } // 按自定义目录 else if (StringUtil.isNotEmpty(pathTypeModel.getFolder()) && "3".equals(rule)) { String folder = pathTypeModel.getFolder(); folder = folder.replaceAll("\\\\", "/"); relativeFilePath += folder; if (!folder.endsWith("/")) { relativeFilePath += "/"; } } } if (StringUtil.isNotEmpty(relativeFilePath)) { relativeFilePath = StringUtil.replaceMoreStrToOneStr(relativeFilePath, "/"); if (relativeFilePath.startsWith("/")) { relativeFilePath = relativeFilePath.substring(1); } //filePath += relativeFilePath; fileName = relativeFilePath.replaceAll("/", ",") + fileName; } } UploaderVO vo = UploaderVO.builder().fileSize(file.getSize()).fileExtension(fileType).build(); // //上传文件 // if ("im".equalsIgnoreCase(type)){ // type = "imfile"; // } 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)) { fileName = relativeFilePath.replaceAll("/", ",") + fileName; thFilename = relativeFilePath.replaceAll("/", ",") + thFilename; } vo.setName(fileName); vo.setUrl(UploaderUtil.uploaderImg("/api/file/Image/" + type + "/", fileName)); vo.setThumbUrl(UploaderUtil.uploaderImg("/api/file/Image/" + type + "/", thFilename)); //上传到永中 if ("yozo".equals(configValueUtil.getPreviewType())) { // try { // @Cleanup InputStream inputStream = file.getInputStream(); // String s = yozoUtils.uploadFileInPreview(inputStream, orgFileName); // Map map = JsonUtil.stringToMap(s); // if ("操作成功".equals(map.get("message"))) { // Map dataMap = JsonUtil.stringToMap(String.valueOf(map.get("data"))); // String verId = String.valueOf(dataMap.get("fileVersionId")); // vo.setFileVersionId(verId); // } // } catch (Exception e) { // System.out.println("上传到永中失败"); // e.printStackTrace(); // } } 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 chunkFiles = FileUtil.getFile(new File(FileUploadUtils.getLocalBasePath() + filePath)); List 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()); ChunkRes chunkRes = ChunkRes.builder().merge(chunk.getTotalChunks().equals(existsChunk.size())).chunkNumbers(existsChunk).build(); return chunkRes; } @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()) { System.out.println("该分块已经上传:" + chunkFile.getName()); } else { @Cleanup InputStream inputStream = file.getInputStream(); FileUtils.copyInputStreamToFile(inputStream, chunkTmpFile); chunkTmpFile.renameTo(chunkFile); } 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(); } System.out.println("上传异常:" + 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 { List mergeFileList = FileUtil.getFile(new File(filePath)); @Cleanup FileOutputStream destTempfos = new FileOutputStream(partFile, true); 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.replaceAll("\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); //下载文件 // String typePath = FilePathUtil.getFilePath(type.toLowerCase()); // if (fileName.indexOf(",") >= 0) { // typePath += fileName.substring(0, fileName.lastIndexOf(",") + 1).replaceAll(",", "/"); // fileName = fileName.substring(fileName.lastIndexOf(",") + 1); // } fileName = URLUtil.decode(fileName, GlobalConst.DEFAULT_CHARSET); FileParameter fileParameter = new FileParameter(type, fileName); //验证缓存 if (ticketObj != null) { boolean nodelete = false; //某些手机浏览器下载后会有提示窗口, 会访问两次下载地址 if (UserProvider.getDeviceForAgent().equals(DeviceType.APP) && "".equals(ticketObj)) { TicketUtil.updateTicket(cacheKEY, "1", 30L); nodelete = true; } else { TicketUtil.deleteTicket(cacheKEY); } FileUploadUtils.downloadFile(fileParameter, inputStream -> { FileDownloadUtil.outFile(inputStream, downName); }); if (FileTypeConstant.FILEZIPDOWNTEMPPATH.equals(type) && !nodelete) { //删除打包的临时文件,释放存储 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.asList(fileType).stream().filter(type -> type.equals(docType)).findFirst().orElse(null); if (StringUtil.isEmpty(s)) { throw new DataException(MsgCode.FA040.get()); } //解析文件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 = ""; } } // type = getPath(FileTypeConstant.ANNEXPIC); String url; //文件预览策略 if ("yozo".equals(configValueUtil.getPreviewType())) { // if (StringUtil.isEmpty(previewParams.getFileVersionId())) { // throw new DataException(MsgCode.FA041.get()); // } // // String fileVersionId = XSSEscape.escape(previewParams.getFileVersionId()); // // //获取签名 // Map parameter = new HashMap(); // parameter.put("appId", new String[]{YozoParams.APP_ID}); // parameter.put("fileVersionId", new String[]{fileVersionId}); // String sign = yozoUtils.generateSign(YozoParams.APP_ID, YozoParams.APP_KEY, parameter).getData(); // url = "http://eic.yozocloud.cn/api/view/file?fileVersionId=" // + fileVersionId // + "&appId=" // + YozoParams.APP_ID // + "&sign=" // + sign; url = ""; } else { // String[] split = fileNameAll.split("/"); // if (split.length > 5) { // type = FilePathUtil.getFilePath(type); // String fName = split[5]; // if (fName.contains(",")) { // fName = fName.replaceAll(",", "/"); // type += fName.substring(0, fName.lastIndexOf("/") + 1); // fileName = fName.substring(fName.lastIndexOf("/") + 1); // } // } // url = configValueUtil.getApiDomain() + UploaderUtil.uploaderFile(fileName + "#" + type) + "&name=" + fileName + "&fullfilename=" + fileName; String downFileName = fileName; if (downFileName.contains(",")) { downFileName = downFileName.substring(downFileName.lastIndexOf(",") + 1); } url = configValueUtil.getApiDomain() + "/api/file/Image/" + type + "/" + fileName + "?fullfilename=" + downFileName + "&s=" + UserProvider.getUser().getSecurityKey();// + "&name=" + fileName + "&fullfilename=" + fileName; //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) { //目录校验, 开放UserAvatar、BiVisualPath 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 (StringUtil.isNotEmpty(securityKey) && redirect) { String downName = fileName; if (downName.contains(",")) { downName = downName.substring(downName.lastIndexOf(",") + 1); } String urlEncodeFileName = URLUtil.encode(fileName, GlobalConst.DEFAULT_CHARSET); String urlEncodeDownName = URLUtil.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 RuntimeException(e); } } else { FileUploadUtils.downloadFile(new FileParameter(type, fileName), inputStream -> { FileDownloadUtil.flushFile(inputStream, fileName); }); } } }