package com.product.file.service; import cn.hutool.core.date.DateUtil; import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.product.common.lang.StringUtils; import com.product.core.config.CoreConst; import com.product.core.config.Global; import com.product.core.connection.ConnectionManager; import com.product.core.dao.BaseDao; import com.product.core.entity.DataTableEntity; import com.product.core.entity.FieldMetaEntity; import com.product.core.entity.FieldSetEntity; import com.product.core.entity.RequestParameterEntity; import com.product.core.exception.BaseException; import com.product.core.service.support.AbstractBaseService; import com.product.core.spring.context.SpringMVCContextHolder; import com.product.file.config.CmnConst; import com.product.file.config.FileCode; import com.product.file.util.CreateDocumentIndexThread; import com.product.file.util.FileUtil; import com.product.file.util.FileUtils; import com.product.module.sys.entity.SystemUser; import com.product.util.BaseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.*; /** * Copyright LX-BASE * * @Title: FileManagerService * @Project: LX-BASE-SERVER * @Date: 2020-06-16 10:30 * @Author: LiuChao * @Description: 文件管理 */ @Service("fileManagerService") public class FileManagerService extends AbstractBaseService { @Autowired public BaseDao baseDao; @Autowired FileUtils fileUtils; /** * 获取静态资源 * * @param uuid * @param response * @throws IOException * @throws BaseException */ public void getStaticFile(String uuid, HttpServletResponse response) throws IOException, BaseException { String systemConfig = Global.getSystemConfig("file.static", ""); if (StringUtils.isEmpty(uuid) || StringUtils.isEmpty(systemConfig)) { throw new BaseException(FileCode.GET_FILE_FAIL); } FieldSetEntity fs = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, uuid, false); if (fs == null || StringUtils.isEmpty(fs.getString("attachment_data_table")) || StringUtils.isEmpty(fs.getString("attachment_data_field"))) { throw new BaseException(FileCode.GET_FILE_FAIL); } List list = new ArrayList<>(Arrays.asList(systemConfig.split(","))); if (list != null && list.size() > 0) { if (list.contains((fs.getString("attachment_data_table") + "." + fs.getString("attachment_data_field")))) { // 允许的静态资源 this.getFileContent(fs, response); return; } } throw new BaseException(FileCode.GET_FILE_FAIL); } /** * 文件记录-文件新增结果回调: * result-1 服务器新增成功,修改状态 * result-其他 服务器新增失败,删除准备新增的记录 * * @param fse 参数对象 * @return 结果 * @throws BaseException 基本异常 */ public JSONObject addFileCallback(FieldSetEntity fse) throws BaseException { String result = fse.getString(CmnConst.RESULT); JSONObject returnResult = new JSONObject(); if ("1".equals(result)) { returnResult.put(CmnConst.TYPE, "update"); returnResult.put(CmnConst.RESULT, updateFileStatus(fse.getString("uuid"), 2) != null); } else { returnResult.put(CmnConst.TYPE, "del"); returnResult.put(CmnConst.RESULT, baseDao.delete(CmnConst.PRODUCT_SYS_ATTACHMENTS, new String[]{fse.getString("uuid")})); } return returnResult; } /** * 文件记录-删除准备(状态修改) * * @param fse 参数对象 * @return 待删除的文件记录fse * @throws BaseException 基本异常 */ public FieldSetEntity delFileRecordPlan(FieldSetEntity fse) throws BaseException { return updateFileStatus(fse.getString("uuid"), 3); } /** * 文件记录-文件删除回调: * result-1 服务器删除成功,删除本地记录 * result-其他 服务器删除失败,还原本地记录 * * @param fse 参数对象 * @return 结果 * @throws BaseException 基本异常 */ public JSONObject delFileCallback(FieldSetEntity fse) throws BaseException { String result = fse.getString(CmnConst.RESULT); JSONObject returnResult = new JSONObject(); if ("1".equals(result)) { returnResult.put(CmnConst.TYPE, "del"); returnResult.put(CmnConst.RESULT, baseDao.delete(CmnConst.PRODUCT_SYS_ATTACHMENTS, new String[]{fse.getString("uuid")})); } else { returnResult.put(CmnConst.TYPE, "update"); returnResult.put(CmnConst.RESULT, updateFileStatus(fse.getString("uuid"), 2) != null); } return returnResult; } /** * 修改文件状态 * * @param fileUUID 文件UUID * @param status 待修改的状态 * @return 是否成功 * @throws BaseException 基本异常 */ private FieldSetEntity updateFileStatus(String fileUUID, int status) throws BaseException { FieldSetEntity fse = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, fileUUID, false); fse.setValue("opt_flat", status); fse.setValue(CmnConst.UPDATED_BY, SpringMVCContextHolder.getCurrentUser().getUser_id()); fse.setValue(CmnConst.UPDATED_UTC_DATETIME, new Date()); baseDao.update(fse); fse.setValue(CmnConst.SOURCE_DOMAIN, Global.getSystemConfig(CmnConst.SOURCE_DOMAIN, "")); return fse; } /** * 获取客户上传的文件总大小 * * @param clientUUID 客户UUID * @return 已上传文件总大小 * @throws BaseException 基本异常 */ public long findClientFileUsedCapacity(String clientUUID) throws BaseException { StringBuilder sql = new StringBuilder() .append("\nselect sum(attachment_size) usedCapacity from ").append(CmnConst.PRODUCT_SYS_ATTACHMENTS) .append("\nwhere client_uuid=?"); DataTableEntity dt = baseDao.listTable(sql.toString(), new Object[]{clientUUID}); FieldSetEntity fse; long usedCapacity = 0; if (dt.getRows() == 1) { fse = dt.getFieldSetEntity(0); usedCapacity = fse.getLong("usedCapacity") == null ? 0 : fse.getLong("usedCapacity"); } return usedCapacity; } /** * 获取客户剩余的文件总大小 * * @return 剩余空间大小 */ public long findClientFileResidueCapacity(String CLIENT_UUID) throws BaseException { FieldSetEntity clientFse = baseDao.getFieldSetEntityByFilter(CmnConst.PRODUCT_SYS_CLIENTS, "uuid=?", new String[]{CLIENT_UUID}, false); return findClientFileResidueCapacity(clientFse); } /** * 获取客户剩余的文件总大小 * * @param fse 客户fse * @return 剩余空间大小 */ public long findClientFileResidueCapacity(FieldSetEntity fse) throws BaseException { long clientCapacity = fse.getLong(CmnConst.ATTACHMENT_CAPACITY) == null ? 0 : fse.getInteger(CmnConst.ATTACHMENT_CAPACITY); long usedCapacity = findClientFileUsedCapacity(fse.getString("uuid")); return clientCapacity - usedCapacity; } /** * 验证是否允许上传的文件类型 * * @param attachmentTitle 文件名称 * @return 是否 */ public boolean checkIsAllowedFileType(String attachmentTitle) { if (StringUtils.isEmpty(attachmentTitle) || attachmentTitle.lastIndexOf(".") < 0 || attachmentTitle.length() < 1) { return false; } String curFileTail = attachmentTitle.substring(attachmentTitle.lastIndexOf(".") + 1, attachmentTitle.length()); String allowedFileTypes = Global.getSystemConfig("allowed_file_type", "").toLowerCase(Locale.ROOT); return allowedFileTypes != null && ("," + allowedFileTypes + ",").contains("," + curFileTail.toLowerCase() + ","); } /** * 获取原始域名 * * @return 原始域名 */ public String findSourceDomain() { return Global.getSystemConfig(CmnConst.SOURCE_DOMAIN, ""); } /** * 定时任务:自动清理无用数据 */ public boolean autoClearNoUsedData() throws BaseException { StringBuilder sql = new StringBuilder() .append("\ndelete from ").append(CmnConst.PRODUCT_SYS_ATTACHMENTS) .append("\nwhere opt_flat in (1,3)") .append("\nand ").append(CmnConst.CREATED_BY).append("<=date_format(adddate(now(),-1),'%Y-%m-%d')"); return baseDao.executeUpdate(sql.toString()); } /** * 根据附件uuid判断文件是否存在 * * @param uuid * @return */ public boolean fileExist(String uuid) { if (StringUtils.isEmpty(uuid)) { return false; } FieldSetEntity attachmentFse = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, uuid, false); if (attachmentFse == null) { return false; } //判断文件存放在服务器还是本地 boolean needDownloadFromServerFlag = "1".equals(attachmentFse.getString(CmnConst.UPLOAD_SIGN)); String dir = attachmentFse.getString(CmnConst.ATTACHMENT_URL); String fileName = attachmentFse.getString(CmnConst.ATTACHMENT_TITLE); boolean encrptSignFlag = "1".equals(attachmentFse.getString(CmnConst.ENCRPT_SIGN)); if (needDownloadFromServerFlag) { // 需要从附件服务器上取文件 FTPService ftpService = new FTPService(); return ftpService.fileIsExist(dir, fileName); } else { // 直接在本地的目录中找文件 String localBasePath = Global.getSystemConfig("local.dir", ""); String path = localBasePath + File.separator + dir + File.separator + fileName; File file = new File(path); return file.isFile(); } } /** * 上传文件到本地服务器 * 禁止修改任何逻辑!!!!!! * * @param rpe * @return */ public FieldSetEntity uploadFile(RequestParameterEntity rpe) { FieldSetEntity fse = rpe.getFormData().clones(); Map fileMap = rpe.getFiles(); SystemUser user = SpringMVCContextHolder.getCurrentUser(); String clientUuid = user == null ? fse.getString("client_uuid") : user.getClient_uuid(); // long residueCapacity = findClientFileResidueCapacity(clientUuid); // long fileTotalSize = 0; for (Map.Entry entry : fileMap.entrySet()) { // 格式验证 if (!checkIsAllowedFileType(entry.getKey())) { throw new BaseException(FileCode.ADD_FILE_NOT_ALLOWED.getValue(), FileCode.ADD_FILE_NOT_ALLOWED.getText() + ":" + entry.getKey().substring(entry.getKey().indexOf(".") + 1)); } // fileTotalSize += (int) entry.getValue().length(); } // 剩余空间判定 // if (residueCapacity < fileTotalSize) { // throw new BaseException(FileCode.ADD_FILE_NO_CAPACITY.getValue(), FileCode.ADD_FILE_NO_CAPACITY.getText()); // } Map map = new HashMap<>(fse.getValues()); ConnectionManager.getConnection(); FieldMetaEntity fieldMetaEntity = fse.getMeta(); Object o; String fieldName = fse.getString("~field_name~"); Object value; String fileNames; FieldSetEntity fieldFse; File tempFile; File localTempFile = null; long fileLength = 0; FTPService ftpService; boolean needUpload2FileServerFlag = FTPService.needUpload2FileServer(); String templateType = fse.getString("template_type"); String dir; if (StringUtils.isEmpty(templateType)) { String timeStr = new SimpleDateFormat("yyyyMMdd").format(new Date()); dir = clientUuid + File.separator + timeStr; } else { dir = CmnConst.DIR_TEMPLATE + File.separator + ("1".equals(templateType) ? CmnConst.DIR_IMPORT : ("2".equals(templateType) ? CmnConst.DIR_PRINT : CmnConst.DIR_OTHER)); } String uuids = ""; for (Map.Entry entry : map.entrySet()) { fieldFse = fieldMetaEntity.getFieldMeta(fieldName); if (fieldFse == null || (!CmnConst.ATTACHMENT_TYPE.equals(fieldFse.getString(CmnConst.FIELD_TYPE)) && !"file-image".equals(fieldFse.getString(CmnConst.FIELD_TYPE)))) { fse.setValue(entry.getKey().toString(), null); continue; } // 已经判定为附件,进行操作 value = entry.getValue(); if (value == null || StringUtils.isEmpty(fileNames = value.toString())) { fse.setValue(entry.getKey().toString(), null); continue; } for (String fileName : fileNames.split(",")) { if (StringUtils.isEmpty(fieldName)) { fse.setValue(entry.getKey().toString(), null); continue; } tempFile = fileMap.get(fileName); if (tempFile == null) { //不是文件 跳过 continue; } fileLength = tempFile.length(); String tail = fileName.substring(fileName.lastIndexOf(".") + 1); int viewOnlineSign = 0; if (("," + Global.getSystemConfig("can.direct.view.online.format", "") + ",").contains("," + tail + ",")) { viewOnlineSign = 2; } else { if (Global.getPropertyToBoolean("file.view.online", "true") && ("," + Global.getSystemConfig("can.transfer.format", "") + ",").contains("," + tail + ",") && StringUtils.isEmpty(templateType)) { viewOnlineSign = 1; } } final String fileFinalName = FileUtils.uploadFile(tempFile, templateType, clientUuid); // 记录附件信息到数据库 logger.info("正在记录附件信息到数据库..."); FieldSetEntity dictFse = baseDao.getFieldSetEntityByFilter(CmnConst.PRODUCT_SYS_DICT, new String[]{CmnConst.UUID, CmnConst.DICT_VALUE}, "lower( " + CmnConst.DICT_VALUE + ")=lower(?) and dict_name='upload_file_format' and is_used=1", new Object[]{tail}, false, ""); FieldSetEntity attachmentFse = new FieldSetEntity(); attachmentFse.setTableName(CmnConst.PRODUCT_SYS_ATTACHMENTS); String fileType; if (dictFse != null && !StringUtils.isEmpty(dictFse.getString(CmnConst.UUID))) { attachmentFse.setValue(CmnConst.ATTACHMENT_TYPE_UUID, dictFse.getUUID()); fileType = dictFse.getString(CmnConst.DICT_VALUE); } else { fileType = tail; attachmentFse.setValue(CmnConst.ATTACHMENT_TYPE_UUID, tail); } //允许编辑 int allowEdit = ("," + Global.getSystemConfig("can.web.online.edit", "doc,docx,xls,xlsx.ppt,pptx,wps,cvs,wps,wpt,et,eet") + ",").indexOf("," + fileType + ",") != -1 ? 1 : 0; attachmentFse.setValue(CmnConst.OPT_FLAT, 2); attachmentFse.setValue(CmnConst.FILE_NAME, fileName); attachmentFse.setValue(CmnConst.ATTACHMENT_TITLE, fileFinalName); attachmentFse.setValue(CmnConst.CLIENT_UUID, clientUuid); attachmentFse.setValue(CmnConst.VIEW_ONLINE_SIGN, viewOnlineSign); attachmentFse.setValue(CmnConst.ATTACHMENT_DATA_TABLE, fse.getTableName()); attachmentFse.setValue(CmnConst.ATTACHMENT_DATA_FIELD, fieldName); attachmentFse.setValue(CmnConst.ATTACHMENT_URL, dir.replaceAll("\\\\", "/")); attachmentFse.setValue(CmnConst.ENCRPT_SIGN, Global.getPropertyToBoolean("file.encrypt", "true") ? 1 : 0); attachmentFse.setValue(CmnConst.ATTACHMENT_SIZE, fileLength); attachmentFse.setValue(CmnConst.UPLOAD_SIGN, needUpload2FileServerFlag ? 1 : 0); attachmentFse.setValue(CmnConst.FUNCTION_UUID, fse.getString(CmnConst.FUNCTION_UUID)); attachmentFse.setValue(CmnConst.ATTACHMENT_CONTAINER, fse.getString(CmnConst.ATTACHMENT_CONTAINER)); attachmentFse.setValue(CmnConst.ATTACHMENT_DOMAIN, fse.getString(CmnConst.ATTACHMENT_DOMAIN)); attachmentFse.setValue(CmnConst.MODULE_UUID, fse.getString(CmnConst.MODULE_UUID)); //luoxin 未获取当前人id 就直接覆1 try { attachmentFse.setValue(CmnConst.CREATED_BY, SpringMVCContextHolder.getCurrentUser().getUser_id()); } catch (Exception e) { e.getStackTrace(); attachmentFse.setValue(CmnConst.CREATED_BY, 1); } attachmentFse.setValue(CmnConst.CREATED_UTC_DATETIME, new Date()); baseDao.saveFieldSetEntity(attachmentFse); logger.info("记录成功"); String uuid = attachmentFse.getUUID(); FieldSetEntity fseIndex = new FieldSetEntity(); fseIndex.setTableName("fseIndex"); fseIndex.setValue("function_uuid", fse.getString("function_uuid")); fseIndex.setValue("attachment_uuid", uuid); CreateDocumentIndexThread.getInstance().appendAttaInfo(fseIndex); fse.setValue(uuid, fileName); fse.setValue(entry.getKey().toString(), uuid); fse.setValue(entry.getKey().toString() + "_edit", allowEdit); fse.setValue(entry.getKey().toString() + "_type", fileType); if (!StringUtils.isEmpty(uuids)) { uuids += ","; } uuids += uuid; } logger.info("正在回写uuid..."); System.out.println(uuids); fse.setValue(fieldName, uuids); } return fse; } /** * 根据附件表数据生成文档索引 */ public void readAttachmentCreateIndex() { // 遍历附件表 DataTableEntity dtAttachment = baseDao.listTable(CmnConst.PRODUCT_SYS_ATTACHMENTS); if (BaseUtil.dataTableIsEmpty(dtAttachment)) { return; } for (int i = 0; i < dtAttachment.getRows(); i++) { // 获取附件信息,并判断是否有表和字段 FieldSetEntity fseAttachment = dtAttachment.getFieldSetEntity(i); if (BaseUtil.strIsNull(fseAttachment.getString("attachment_data_field")) || BaseUtil.strIsNull(fseAttachment.getString("attachment_data_field"))) { continue; } // 查询原数据,有原数据再生成文档检索 FieldSetEntity fseRecord = baseDao.getFieldSetEntityByFilter(fseAttachment.getString("attachment_data_table"), fseAttachment.getString("attachment_data_field") + " LIKE ?", new Object[]{"%" + fseAttachment.getUUID() + "%"}, false); if (fseRecord != null) { // 生成文档检索信息 FieldSetEntity fseIndex = new FieldSetEntity(); fseIndex.setTableName("fseIndex"); fseIndex.setValue("function_uuid", fseAttachment.getString("function_uuid")); fseIndex.setValue("attachment_uuid", fseAttachment.getUUID()); CreateDocumentIndexThread.getInstance().appendAttaInfo(fseIndex); } } } /** * 移动端升级,安装包下载 * * @param fse * @param response */ public void getFileContent(FieldSetEntity fse, HttpServletResponse response,boolean isUpgrade) throws IOException { if(isUpgrade) { FieldSetEntity attachmentFse = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, fse.getUUID(), false); if (attachmentFse == null || !"product_sys_app_version".equals(attachmentFse.getString("attachment_data_table")) ) { return; } logger.info("正在获取文件流..."); boolean needDownloadFromServerFlag = "1".equals(attachmentFse.getString(CmnConst.UPLOAD_SIGN)); String dir = attachmentFse.getString(CmnConst.ATTACHMENT_URL); String fileName = attachmentFse.getString(CmnConst.ATTACHMENT_TITLE); //真实文件名 String realFileName = attachmentFse.getString(CmnConst.FILE_NAME); String viewOnlineSign = attachmentFse.getString(CmnConst.VIEW_ONLINE_SIGN); boolean encrptSignFlag = "1".equals(attachmentFse.getString(CmnConst.ENCRPT_SIGN)); boolean needOnlineViewFlag = "1".equals(fse.getString(CmnConst.NEED_ONLINE_VIEW)) && "1".equals(viewOnlineSign); String clientType =CoreConst.CLIENT_TYPE_APP; String contentType = "multipart/form-data"; boolean isExcel = realFileName.endsWith(".xlsx") || realFileName.endsWith(".xls"); if (needOnlineViewFlag) { //特殊处理: 如果客户端不是App 但预览的文件是Excel 直接返回excel源文件 content头标识是excel cheng 2025年2月11日10:41:41 if (isExcel && !CoreConst.CLIENT_TYPE_APP.equals(clientType)) { contentType = "application/vnd.ms-excel"; } else { dir += File.separator + CmnConst.TRANSFER_DIR_NAME; } } String path = dir + File.separator + fileName; if (needDownloadFromServerFlag) { // 需要从附件服务器上取文件 FTPService ftpService = new FTPService(); logger.info("需要从附件服务器上取文件..."); response.setHeader("Access-Control-Expose-Headers", "*"); response.setContentType(contentType); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realFileName, "UTF-8")); //设置头中的文件类型 if (!needOnlineViewFlag && !encrptSignFlag) { logger.info("不需要解密且获取原文件..."); try (ServletOutputStream os = response.getOutputStream()) { ftpService.downloadFile(path, os); } } else { logger.info("需要解密或者获取转换后的文件..."); String localTempPath = Global.getSystemConfig("temp.dir", "") + File.separator + fileName; File localTempFile = new File(localTempPath); OutputStream tempOs = new FileOutputStream(localTempFile); ftpService.downloadFile(path, tempOs); tempOs.flush(); tempOs.close(); if (needOnlineViewFlag && localTempFile.length() == 0L) { // 若是预览,但是转换后的文件不存在,那么重新执行转换操作 File sourceFile = fileUtils.getFile(true, attachmentFse.getString(CmnConst.ATTACHMENT_URL), fileName, encrptSignFlag); File tempSourceFile = new File(sourceFile.getParentFile().getAbsolutePath() + File.separator + attachmentFse.getString(CmnConst.FILE_NAME)); sourceFile.renameTo(tempSourceFile); FileUtils.convertPdf(false, true, encrptSignFlag, true, tempSourceFile, dir, fileName, attachmentFse.getString(CmnConst.FILE_NAME)); tempSourceFile.delete(); // 重新拉文件 tempOs = new FileOutputStream(localTempFile); ftpService = new FTPService(); ftpService.downloadFile(path, tempOs); tempOs.flush(); tempOs.close(); } response.setContentLengthLong(localTempFile.length()); InputStream is = new FileInputStream(localTempFile); int len; byte[] b = new byte[1024]; try (ServletOutputStream os = response.getOutputStream()) { while ((len = is.read(b)) > 0) { os.write(encrptSignFlag ? FileUtil.decryption(b) : b, 0, len); } os.flush(); is.close(); } // 删除临时文件 localTempFile.delete(); } } else { // 直接在本地的目录中找文件 logger.info("直接在本地的目录中找文件..."); String localBasePath = Global.getSystemConfig("local.dir", ""); path = localBasePath + File.separator + path; File file = new File(path); if (needOnlineViewFlag && file.exists() && CoreConst.CLIENT_TYPE_APP.equals(clientType)) { //特殊处理: 是App 但预览的文件是Excel 需要将之前已转换为的文件删除掉重新转换(因转换后的格式不是pdf) String changeTime = "2025-02-11 23:59:59"; Date changeDate = DateUtil.parse(changeTime, "yyyy-MM-dd HH:mm:ss"); long fileTime = file.lastModified(); //如果时间是 2025年2月10日23:59:59,那么就重新转换 //删除文件 if (fileTime <= changeDate.getTime()) { file.delete(); } } if (needOnlineViewFlag && !file.exists()) { // 若是预览,但是转换后的文件不存在,那么重新执行转换操作 File sourceFile = fileUtils.getFile(false, attachmentFse.getString(CmnConst.ATTACHMENT_URL), fileName, encrptSignFlag); File tempSourceFile = new File(sourceFile.getParentFile().getAbsolutePath() + File.separator + attachmentFse.getString(CmnConst.FILE_NAME)); sourceFile.renameTo(tempSourceFile); FileUtils.convertPdf(false, false, encrptSignFlag, true, tempSourceFile, dir, fileName, attachmentFse.getString(CmnConst.FILE_NAME)); tempSourceFile.delete(); } int len; byte[] b = new byte[1024]; InputStream is = new FileInputStream(file); response.setHeader("Access-Control-Expose-Headers", "*"); response.setContentType(contentType); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realFileName, "UTF-8")); response.setContentLengthLong(file.length()); try (ServletOutputStream os = response.getOutputStream()) { while ((len = is.read(b)) > 0) { if (encrptSignFlag) { // 需要解密 logger.info("需要解密..."); os.write(FileUtil.decryption(b), 0, len); } else { // 无需解密 os.write(b, 0, len); } } os.flush(); } is.close(); } logger.info("文件流获取成功"); } } /** * 提取文件信息-下载文件或者在线预览文件 * * @param fse * @param response */ public void getFileContent(FieldSetEntity fse, HttpServletResponse response) throws IOException { logger.info("正在获取文件流..."); String uuid = fse.getUUID(); FieldSetEntity attachmentFse = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, uuid, false); if (attachmentFse == null) { return; } boolean needDownloadFromServerFlag = "1".equals(attachmentFse.getString(CmnConst.UPLOAD_SIGN)); String dir = attachmentFse.getString(CmnConst.ATTACHMENT_URL); String fileName = attachmentFse.getString(CmnConst.ATTACHMENT_TITLE); //真实文件名 String realFileName = attachmentFse.getString(CmnConst.FILE_NAME); String viewOnlineSign = attachmentFse.getString(CmnConst.VIEW_ONLINE_SIGN); boolean encrptSignFlag = "1".equals(attachmentFse.getString(CmnConst.ENCRPT_SIGN)); boolean needOnlineViewFlag = "1".equals(fse.getString(CmnConst.NEED_ONLINE_VIEW)) && "1".equals(viewOnlineSign); String clientType = SpringMVCContextHolder.getCurrentUser().getClientType(); String contentType = "multipart/form-data"; boolean isExcel = realFileName.endsWith(".xlsx") || realFileName.endsWith(".xls"); if (needOnlineViewFlag) { //特殊处理: 如果客户端不是App 但预览的文件是Excel 直接返回excel源文件 content头标识是excel cheng 2025年2月11日10:41:41 if (isExcel && !CoreConst.CLIENT_TYPE_APP.equals(clientType)) { contentType = "application/vnd.ms-excel"; } else { dir += File.separator + CmnConst.TRANSFER_DIR_NAME; } } String path = dir + File.separator + fileName; if (needDownloadFromServerFlag) { // 需要从附件服务器上取文件 FTPService ftpService = new FTPService(); logger.info("需要从附件服务器上取文件..."); response.setHeader("Access-Control-Expose-Headers", "*"); response.setContentType(contentType); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realFileName, "UTF-8")); //设置头中的文件类型 if (!needOnlineViewFlag && !encrptSignFlag) { logger.info("不需要解密且获取原文件..."); try (ServletOutputStream os = response.getOutputStream()) { ftpService.downloadFile(path, os); } } else { logger.info("需要解密或者获取转换后的文件..."); String localTempPath = Global.getSystemConfig("temp.dir", "") + File.separator + fileName; File localTempFile = new File(localTempPath); OutputStream tempOs = new FileOutputStream(localTempFile); ftpService.downloadFile(path, tempOs); tempOs.flush(); tempOs.close(); if (needOnlineViewFlag && localTempFile.length() == 0L) { // 若是预览,但是转换后的文件不存在,那么重新执行转换操作 File sourceFile = fileUtils.getFile(true, attachmentFse.getString(CmnConst.ATTACHMENT_URL), fileName, encrptSignFlag); File tempSourceFile = new File(sourceFile.getParentFile().getAbsolutePath() + File.separator + attachmentFse.getString(CmnConst.FILE_NAME)); sourceFile.renameTo(tempSourceFile); FileUtils.convertPdf(false, true, encrptSignFlag, true, tempSourceFile, dir, fileName, attachmentFse.getString(CmnConst.FILE_NAME)); tempSourceFile.delete(); // 重新拉文件 tempOs = new FileOutputStream(localTempFile); ftpService = new FTPService(); ftpService.downloadFile(path, tempOs); tempOs.flush(); tempOs.close(); } response.setContentLengthLong(localTempFile.length()); InputStream is = new FileInputStream(localTempFile); int len; byte[] b = new byte[1024]; try (ServletOutputStream os = response.getOutputStream()) { while ((len = is.read(b)) > 0) { os.write(encrptSignFlag ? FileUtil.decryption(b) : b, 0, len); } os.flush(); is.close(); } // 删除临时文件 localTempFile.delete(); } } else { // 直接在本地的目录中找文件 logger.info("直接在本地的目录中找文件..."); String localBasePath = Global.getSystemConfig("local.dir", ""); path = localBasePath + File.separator + path; File file = new File(path); if (needOnlineViewFlag && file.exists() && CoreConst.CLIENT_TYPE_APP.equals(clientType)) { //特殊处理: 是App 但预览的文件是Excel 需要将之前已转换为的文件删除掉重新转换(因转换后的格式不是pdf) String changeTime = "2025-02-11 23:59:59"; Date changeDate = DateUtil.parse(changeTime, "yyyy-MM-dd HH:mm:ss"); long fileTime = file.lastModified(); //如果时间是 2025年2月10日23:59:59,那么就重新转换 //删除文件 if (fileTime <= changeDate.getTime()) { file.delete(); } } if (needOnlineViewFlag && !file.exists()) { // 若是预览,但是转换后的文件不存在,那么重新执行转换操作 File sourceFile = fileUtils.getFile(false, attachmentFse.getString(CmnConst.ATTACHMENT_URL), fileName, encrptSignFlag); File tempSourceFile = new File(sourceFile.getParentFile().getAbsolutePath() + File.separator + attachmentFse.getString(CmnConst.FILE_NAME)); sourceFile.renameTo(tempSourceFile); FileUtils.convertPdf(false, false, encrptSignFlag, true, tempSourceFile, dir, fileName, attachmentFse.getString(CmnConst.FILE_NAME)); tempSourceFile.delete(); } int len; byte[] b = new byte[1024]; InputStream is = new FileInputStream(file); response.setHeader("Access-Control-Expose-Headers", "*"); response.setContentType(contentType); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realFileName, "UTF-8")); response.setContentLengthLong(file.length()); try (ServletOutputStream os = response.getOutputStream()) { while ((len = is.read(b)) > 0) { if (encrptSignFlag) { // 需要解密 logger.info("需要解密..."); os.write(FileUtil.decryption(b), 0, len); } else { // 无需解密 os.write(b, 0, len); } } os.flush(); } is.close(); } logger.info("文件流获取成功"); } /** * 获取文件字节 根据文件uuid */ public byte[] getFileContent(String uuid) throws BaseException { FieldSetEntity attachmentFse = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, uuid, false); return this.getFileContent(attachmentFse); } /** * 根据附件uuid获取文件 * 用完之后需要删除返回的 file (临时文件) * * @param uuid * @return * @throws BaseException */ public File getFile(String uuid) throws BaseException { FieldSetEntity attachmentFse = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, uuid, false); return getFile(attachmentFse); } public File getFile(FieldSetEntity attachmentFse) throws BaseException { byte[] fileContent = getFileContent(attachmentFse); if (fileContent != null && fileContent.length > 0) { String localTempPath = Global.getSystemConfig("temp.dir", "") + File.separator + new Date().getTime() + attachmentFse.getString(CmnConst.ATTACHMENT_TITLE) + attachmentFse.getString("file_name"); File temp = new File(localTempPath); File td = new File(Global.getSystemConfig("temp.dir", "")); if (!td.exists()) td.mkdirs(); try { temp.createNewFile(); } catch (IOException e) { throw new BaseException(FileCode.GET_FILE_FAIL.getValue(), FileCode.GET_FILE_FAIL.getText() + (e.getMessage() != null ? e.getMessage() + ",uuid:" + attachmentFse.getUUID() : "")); } try (FileOutputStream fos = new FileOutputStream(temp); BufferedOutputStream out = new BufferedOutputStream(fos)) { out.write(fileContent); return temp; } catch (Exception e) { e.printStackTrace(); throw new BaseException(FileCode.GET_FILE_FAIL.getValue(), FileCode.GET_FILE_FAIL.getText() + (e.getMessage() != null ? e.getMessage() + ",uuid:" + attachmentFse.getUUID() : "")); } } throw new BaseException(FileCode.GET_FILE_FAIL.getValue(), FileCode.GET_FILE_FAIL.getText() + ",uuid:" + attachmentFse.getUUID()); } /** * 获取文件字节 根据附件field */ public byte[] getFileContent(FieldSetEntity attachmentFse) throws BaseException { if (attachmentFse == null || !CmnConst.PRODUCT_SYS_ATTACHMENTS.equals(attachmentFse.getTableName())) { //返回一个空的字节数组 return new byte[0]; } try (ByteArrayOutputStream os = new ByteArrayOutputStream();) { FTPService ftpService = new FTPService(); boolean needDownloadFromServerFlag = "1".equals(attachmentFse.getString(CmnConst.UPLOAD_SIGN)); String dir = attachmentFse.getString(CmnConst.ATTACHMENT_URL); String fileName = attachmentFse.getString(CmnConst.ATTACHMENT_TITLE); boolean encrptSignFlag = "1".equals(attachmentFse.getString(CmnConst.ENCRPT_SIGN)); String path = dir + File.separator + fileName; if (needDownloadFromServerFlag) { // 需要从附件服务器上取文件 if (encrptSignFlag) { // 需要解密 String localTempPath = Global.getSystemConfig("temp.dir", "") + File.separator + fileName; File localTempFile = new File(localTempPath); OutputStream tempOs = new FileOutputStream(localTempFile); ftpService.downloadFile(path, tempOs); tempOs.flush(); tempOs.close(); InputStream is = new FileInputStream(localTempFile); int len; byte[] b = new byte[1024]; while ((len = is.read(b)) > 0) { os.write(FileUtil.decryption(b), 0, len); } os.flush(); is.close(); // 删除临时文件 localTempFile.delete(); } else { ftpService.downloadFile(path, os); } } else { // 直接在本地的目录中找文件 String localBasePath = Global.getSystemConfig("local.dir", ""); path = localBasePath + File.separator + path; File file = new File(path); int len; byte[] b = new byte[1024]; try (InputStream is = new FileInputStream(file)) { while ((len = is.read(b)) > 0) { if (Global.getPropertyToBoolean("file.encrypt", "true")) { // 需要解密 os.write(FileUtil.decryption(b), 0, len); } else { // 无需解密 os.write(b, 0, len); } } } } return os.toByteArray(); } catch (Exception e) { SpringMVCContextHolder.getSystemLogger().error(FileCode.GET_FILE_BYTES_FAIL.getValue(), FileCode.GET_FILE_BYTES_FAIL.getText() + "," + e.getMessage()); throw new BaseException(FileCode.GET_FILE_BYTES_FAIL.getValue(), FileCode.GET_FILE_BYTES_FAIL.getText() + "," + e.getMessage()); } } /** * 获取文件字节,再转换成字符串 根据文件uuid */ public String getFileContentString(String uuid) { byte[] b = getFileContent(uuid); if (b == null) { return null; } String str = new String(b); return str; } /** * 提取文件信息-批量打包下载文件 * * @param fse * @param */ public void downLoadFileZip(FieldSetEntity fse, HttpServletResponse response) throws IOException, BaseException { logger.info("正在打包下载文件..."); response.setContentType("multipart/form-data"); try (ServletOutputStream os = response.getOutputStream()) { String uuids = fse.getString("uuids"); if (StringUtils.isEmpty(uuids)) { throw new BaseException(FileCode.GET_DATA_FAIL.getValue(), FileCode.GET_DATA_FAIL.getText() + ":uuids"); } DataTableEntity attachmentDte = baseDao.listTable(CmnConst.PRODUCT_SYS_ATTACHMENTS, new Object[]{CmnConst.ATTACHMENT_URL, CmnConst.FILE_NAME, CmnConst.VIEW_ONLINE_SIGN, CmnConst.FILE_NAME, CmnConst.ENCRPT_SIGN, CmnConst.ATTACHMENT_TITLE, CmnConst.UPLOAD_SIGN}, uuids.split(",")); FieldSetEntity attachmentFse; String fileName; String dir; String sourcePath; String localTempPath; FTPService ftpService; boolean needDownloadFromServerFlag; List> pathList = Lists.newArrayList(); Map pathMap; for (int i = 0; i < attachmentDte.getRows(); i++) { pathMap = Maps.newHashMap(); attachmentFse = attachmentDte.getFieldSetEntity(i); needDownloadFromServerFlag = "1".equals(attachmentFse.getString(CmnConst.UPLOAD_SIGN)); fileName = attachmentFse.getString(CmnConst.ATTACHMENT_TITLE); dir = attachmentFse.getString(CmnConst.ATTACHMENT_URL); sourcePath = dir + File.separator + fileName; localTempPath = Global.getSystemConfig("temp.dir", "") + File.separator + fileName; pathMap.put(CmnConst.REAL_FILE_NAME, attachmentFse.getString(CmnConst.FILE_NAME)); pathMap.put(CmnConst.ENCRPT_SIGN, attachmentFse.getString(CmnConst.ENCRPT_SIGN)); if (needDownloadFromServerFlag) { ftpService = new FTPService(); ftpService.downloadFile(sourcePath, localTempPath); pathMap.put(CmnConst.NEED_LOCAL_TEMP_FILE_SIGN, "1"); pathMap.put(CmnConst.SOURCE_PATH, localTempPath); } else { pathMap.put(CmnConst.NEED_LOCAL_TEMP_FILE_SIGN, "0"); pathMap.put(CmnConst.SOURCE_PATH, Global.getSystemConfig("local.dir", "") + File.separator + sourcePath); } pathList.add(pathMap); } long fileSize = FileUtil.createZip(pathList, os); response.setContentLengthLong(fileSize); File file; for (Map map : pathList) { for (String key : map.keySet()) { if ("1".equals(map.get(CmnConst.NEED_LOCAL_TEMP_FILE_SIGN)) && !CmnConst.SOURCE_PATH.equals(key)) { continue; } file = new File(key); file.delete(); } } logger.info("文件流获取成功"); } catch (IOException e) { throw e; } } /** * 删除本地或线上服务器文件 * * @param fse * @return */ public boolean delFiles(FieldSetEntity fse) { FTPService ftpService = new FTPService(); boolean needUpload2FileServerFlag = FTPService.needUpload2FileServer(); String uuid = fse.getString(CmnConst.UUID); boolean needOnlineViewFlag = "1".equals(fse.getString(CmnConst.UPLOAD_SIGN)); FieldSetEntity attachmentFse = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, uuid, false); String path = attachmentFse.getString(CmnConst.ATTACHMENT_URL); if (needUpload2FileServerFlag) { //删除线上服务器文件 ftpService.deleteFile(path, attachmentFse.getString(CmnConst.FILE_NAME)); //if(needOnlineViewFlag){//需要删除转换后的文件 path += File.separator + CmnConst.TRANSFER_DIR_NAME; ftpService.deleteFile(path, attachmentFse.getString(CmnConst.FILE_NAME)); //} } else { //删除本地服务器文件 deleteFiles(Global.getSystemConfig("temp.dir", "") + "/" + attachmentFse.getString(CmnConst.FILE_NAME));// 路径及文件名(或文件夹) deleteFiles(Global.getSystemConfig("temp.dir", "") + "/" + CmnConst.TRANSFER_DIR_NAME + "/" + attachmentFse.getString(CmnConst.FILE_NAME));// 在线预览路径及文件名(或文件夹) deleteFiles(Global.getSystemConfig("local.dir", "") + "/" + attachmentFse.getString(CmnConst.FILE_NAME));// 路径及文件名(或文件夹) deleteFiles(Global.getSystemConfig("local.dir", "") + "/" + CmnConst.TRANSFER_DIR_NAME + "/" + attachmentFse.getString(CmnConst.FILE_NAME));// 在线预览路径及文件名(或文件夹) } //删除数据库记录 return baseDao.delete(CmnConst.PRODUCT_SYS_ATTACHMENTS, new Object[]{uuid}); } /** * 删除单个文件 * * @param pathName 删除文件路径名 * @return */ public boolean deleteFiles(String pathName) { boolean flag = false; //根据路径创建文件对象 File file = new File(pathName); //路径是个文件且不为空时删除文件 if (file.isFile() && file.exists()) { flag = file.delete(); } //删除失败时,返回false return flag; } /** * 删除目录本身以及目录下的所有文件及文件夹 * * @param pathName 目录名 * @return */ public boolean deleteDiretory(String pathName) { boolean flag = false; //根据路径创建文件对象 File directory = new File(pathName); //如果路径是一个目录且不为空时,删除目录 if (directory.isDirectory() && directory.exists()) { //获取目录下的所有的目录和文件,放入数组files中 File[] files = directory.listFiles(); //遍历目录下的所有的文件和目录 for (int i = 0; i < files.length; i++) { //如果目录下是文件时,调用deleteFiles()方法,删除单个文件 if (files[i].isFile()) { flag = deleteFiles(files[i].getAbsolutePath()); }//如果目录下是目录时,调用自身deleteDirectory(),形成递归调用 else { flag = deleteDiretory(files[i].getAbsolutePath()); } } //删除目录本身,如果想要保留目录只删除文件,此句可以不要 flag = directory.delete(); } //删除成功时返回true,失败时返回false return flag; } /** * 删除文件或者目录 * * @param pathName 路径名 * @return */ public boolean deleteDirectoryOrFile(String pathName) { boolean flag = false; File file = new File(pathName); //如果路径是一个文件则调用deleteFiles() if (file.isFile() && file.exists()) { flag = deleteFiles(pathName); }//如果路径是目录则调用deleteDirectory() else if (file.isDirectory() && file.exists()) { flag = deleteDiretory(pathName); } return flag; } }