适配存储到数据库的文件;
不需要存储到数据库的,若是集群,那么同步到集群的其他节点;
若是失败,定时任务重试,至多3次
已修改4个文件
227 ■■■■■ 文件已修改
src/main/java/com/product/file/config/CmnConst.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/file/config/FileCode.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/file/controller/FileManagerController.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/file/service/FileManagerService.java 185 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/file/config/CmnConst.java
@@ -29,6 +29,7 @@
    public static final String TABLE_PRODUCT_SYS_ORG_LEVELS = "product_sys_org_levels";// 组织机构表
    public static final String PRODUCT_OA_DOCUMENT_COLLECTION = "product_oa_document_collection";    // 文档收藏
    public static final String PRODUCT_OA_DOCUMENT_HISTORY = "product_oa_document_history";
    public static final String PRODUCT_SYS_CLUSTER_SYNC_FILE_LOG = "product_sys_cluster_sync_file_log";
    public static final String ATTACHMENT_CAPACITY = "attachment_capacity";
    public static final String RESULT = "result";
@@ -124,4 +125,13 @@
    public static final String DIR_IMPORT = "import";// 数据导入目录
    public static final String DIR_PRINT = "print";// 打印目录
    public static final String DIR_OTHER = "other";// 其他目录
    // 集群同步文件日志
    public static final String ERROR_INFO = "error_info";
    public static final String NEED_RETRY = "need_retry";
    public static final String ATTACHMENT_UUID = "attachment_uuid";
    public static final String TARGET_INFO = "target_info";
    public static final String SOURCE_INFO = "source_info";
    public static final String PRE_UUID = "pre_uuid";
    public static final String RETRY_COUNT = "retry_count";
}
src/main/java/com/product/file/config/FileCode.java
@@ -81,7 +81,11 @@
    ARCHIVE_FILE_TITLE_FIELD_NO_EXIST("归档文件标题字段不存在", ModuleEnum.FILE.getValue() + "058"),
//    归档父目录不存在
    ARCHIVE_FILE_PARENT_DIRECTORY_NO_EXIST("归档父目录不存在", ModuleEnum.FILE.getValue() + "059")
    ARCHIVE_FILE_PARENT_DIRECTORY_NO_EXIST("归档父目录不存在", ModuleEnum.FILE.getValue() + "059"),
    CLUSTER_FILE_SYNC_FAIL("集群文件同步失败", ModuleEnum.FILE.getValue() + "060"),
    CLUSTER_FILE_SYNC_CHECK_FAIL("集群文件同步验证失败", ModuleEnum.FILE.getValue() + "061"),
    FILE_TRANSFER_BYTE_ARRAY_FAIL("文件转化为字节数组失败", ModuleEnum.FILE.getValue() + "062"),
    CLUSTER_FILE_SYNC_ACCEPT_FAIL("集群文件同步方法接收失败", ModuleEnum.FILE.getValue() + "063"),
    ;
src/main/java/com/product/file/controller/FileManagerController.java
@@ -360,7 +360,7 @@
            // 正式代码
            fileManagerService.getFileContent(fse, response);
            /*===test-start===*/
//            String tempPath = fse.getString("tempPath");
//            String tempPath = "";
//            File file = new File(tempPath);
//            OutputStream sos = new FileOutputStream(file);
//            fileManagerService.getFileContent(fse, sos);
@@ -415,4 +415,28 @@
            return error(FileCode.GET_FILE_CONTENT_FAIL.getValue(), FileCode.GET_FILE_CONTENT_FAIL.getText());
        }
    }
    /**
     * 集群文件同步方法-接收
     *
     * @return 结果
     */
    @RequestMapping(value = "/cluster-file-sync/{version}", method = RequestMethod.POST)
    @ApiVersion(1)
    public String clusterFileSync(HttpServletRequest request, HttpServletResponse response) {
        try {
            Object bean = request.getAttribute(CoreConst.API_POST_REQUEST_DATA);
            RequestParameterEntity rpe = (RequestParameterEntity) bean;
            fileManagerService.clusterFileSyncAccept(rpe);
            return OK();
        } catch (BaseException e) {
            SpringMVCContextHolder.getSystemLogger().error(e);
            e.printStackTrace();
            return error(e.getCode(), e.getMessage());
        } catch (Exception e) {
            SpringMVCContextHolder.getSystemLogger().error(e);
            e.printStackTrace();
            return error(FileCode.CLUSTER_FILE_SYNC_ACCEPT_FAIL.getValue(), FileCode.CLUSTER_FILE_SYNC_ACCEPT_FAIL.getText() + ":" + BaseUtil.getErrorInfo(e).substring(0, 500));
        }
    }
}
src/main/java/com/product/file/service/FileManagerService.java
@@ -1,10 +1,12 @@
package com.product.file.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
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.cache.util.RedisUtil;
import com.product.core.config.CoreConst;
import com.product.core.config.Global;
import com.product.core.connection.ConnectionManager;
@@ -25,6 +27,10 @@
import com.product.module.sys.entity.SystemUser;
import com.product.tool.table.enums.FieldType;
import com.product.util.BaseUtil;
import com.product.util.http.HttpRequestUtil;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -33,8 +39,7 @@
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.sql.Blob;
import java.sql.SQLException;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.*;
@@ -281,6 +286,134 @@
    }
    /**
     * 集群文件同步方法-接收
     * @param rpe
     * @return
     */
    public void clusterFileSyncAccept(RequestParameterEntity rpe) throws IOException {
        List<String> ipList = RedisUtil.getSet(CoreConst.SYSTEM_IP_PORT_KEY, String.class);
        String curRequestIp = rpe.getIp();
        boolean checkFlag = false;
        for (String ipInfo : ipList) {
            if (!StringUtils.isEmpty(ipInfo) && ipInfo.contains(":") && curRequestIp.equals(ipInfo.split(":")[0])) {
                checkFlag = true;
                break;
            }
        }
        if (!checkFlag) {
            throw new BaseException(FileCode.CLUSTER_FILE_SYNC_CHECK_FAIL);
        }
        Map<Object, Object> otherMap = rpe.getOther();
        Map<String, File> fileMap = rpe.getFiles();
        File aimFile = new File(Global.getSystemConfig("local.dir", "") + File.separator + otherMap.get("relativePath"));
        for (Map.Entry<String, File> entry : fileMap.entrySet()) {
            File file = entry.getValue();
            Files.copy(file.toPath(), aimFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
        }
    }
    /**
     * 集群文件同步方法-请求调用
     * @param file              同步的文件
     * @param fileName          文件名称,保存的文件名称,就是时间戳+2位随机字符串
     * @param relativePath      相对路径
     * @param attachmentUUID    附近表uuid
     * @param preLogFse         之前的日志fse
     */
    public void clusterFileSyncRequest(File file, String fileName, String relativePath, String attachmentUUID, FieldSetEntity preLogFse) {
        try {
            if (file == null || StringUtils.isEmpty(fileName)) {
                return;
            }
            List<String> ipList = RedisUtil.getSet(CoreConst.SYSTEM_IP_PORT_KEY, String.class);
            String curIpInfo = Global.getSystemConfig(CoreConst.SYSTEM_NAME, "");
            for (String ipInfo : ipList) {
                // 排除当前
                if (curIpInfo.equals(ipInfo)) {
                   continue;
                }
                if (!StringUtils.isEmpty(ipInfo) && ipInfo.contains(":")) {
                    String url = String.format("http://%s/lx/api/fileManager/cluster-file-sync/v1", ipInfo);
                    RequestBody requestBody = new MultipartBody.Builder()
                            .setType(MultipartBody.FORM)
                            // 添加文件字段
                            .addFormDataPart("file", fileName, RequestBody.create(MediaType.parse("application/octet-stream"), file))
                            .addFormDataPart("title", fileName)
                            .addFormDataPart("relativePath", relativePath)
                            .build();
                    JSONObject result = HttpRequestUtil.request(url, "POST", "multipart/form-data", requestBody);
                    SpringMVCContextHolder.getSystemLogger().error("[6ctest]:" + result.toString());
                    // 记录日志
                    recordClusterSyncFileLog(result, attachmentUUID, curIpInfo, ipInfo, preLogFse);
                }
            }
        } catch (Exception e) {
            throw new BaseException(FileCode.CLUSTER_FILE_SYNC_FAIL);
        }
    }
    public void clusterFileSyncRequest(File file, String fileName, String relativePath, String attachmentUUID) {
        clusterFileSyncRequest(file, fileName, relativePath, attachmentUUID, null);
    }
    /**
     * 记录集群同步文件日志
     * @param result            结果,-1-失败,1-成功,2-已重新处理
     * @param attachmentUUID    附件uuid
     * @param sourceIpInfo      请求地址信息
     * @param targetIpInfo      目标地址信息
     * @param preLogFse         之前日志fse
     */
    private void recordClusterSyncFileLog(JSONObject result, String attachmentUUID, String sourceIpInfo, String targetIpInfo, FieldSetEntity preLogFse) {
        FieldSetEntity logFse = new FieldSetEntity();
        logFse.setTableName(CmnConst.PRODUCT_SYS_CLUSTER_SYNC_FILE_LOG);
        if (!"200".equals(result.getString("code"))) {
            logFse.setValue(CmnConst.ERROR_INFO, result.getString("msg"));
            logFse.setValue(CmnConst.RESULT, -1);
            logFse.setValue(CmnConst.NEED_RETRY, 1);
        } else {
            logFse.setValue(CmnConst.RESULT, 1);
            logFse.setValue(CmnConst.NEED_RETRY, -1);
        }
        if (!FieldSetEntity.isEmpty(preLogFse)) {
            logFse.setValue(CmnConst.RETRY_COUNT, preLogFse.getInteger(CmnConst.RETRY_COUNT) == null ? 1 : (preLogFse.getInteger(CmnConst.RETRY_COUNT) + 1));
            logFse.setValue(CmnConst.PRE_UUID, preLogFse.getUUID());
        } else {
            logFse.setValue(CmnConst.RETRY_COUNT, 0);
        }
        logFse.setValue(CmnConst.ATTACHMENT_UUID, attachmentUUID);
        logFse.setValue(CmnConst.SOURCE_INFO, sourceIpInfo);
        logFse.setValue(CmnConst.TARGET_INFO, targetIpInfo);
        logFse.setValue(CmnConst.CREATED_UTC_DATETIME, new Date());
        SystemUser curUser = SpringMVCContextHolder.getCurrentUser();
        logFse.setValue(CmnConst.CREATED_BY, curUser == null ? -1 : curUser.getUser_id());
        baseDao.saveFieldSetEntity(logFse);
    }
    /**
     * 定时任务触发-扫描集群同步文件日志记录表,重试未达最大次数的错误日志
     */
    public void retryClusterSyncFileFailLog() {
        // 最大重试次数
        int maxRetryCount = 3;
        // 当前地址信息
        String curIpInfo = Global.getSystemConfig(CoreConst.SYSTEM_NAME, "");
        DataTableEntity waitRetryDte = baseDao.listTable(CmnConst.PRODUCT_SYS_CLUSTER_SYNC_FILE_LOG, "result=-1 AND retry_count<? AND need_retry=1 AND source_info=?", new Object[]{maxRetryCount, curIpInfo});
        for (int i = 0; i < waitRetryDte.getRows(); i++) {
            FieldSetEntity waitRetryFse = waitRetryDte.getFieldSetEntity(i);
            FieldSetEntity attachmentFse = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, waitRetryFse.getString(CmnConst.ATTACHMENT_UUID), false);
            String fileName = attachmentFse.getString("attachment_title");
            String relativePath = attachmentFse.getString("attachment_url") + File.separator + fileName;
            File aimFile = new File(Global.getSystemConfig("local.dir", "") + File.separator + relativePath);
            clusterFileSyncRequest(aimFile, fileName, relativePath, attachmentFse.getUUID(), waitRetryFse);
            waitRetryFse.setValue(CmnConst.RESULT, 2);
            waitRetryFse.setValue(CmnConst.NEED_RETRY, -1);
            baseDao.saveFieldSetEntity(waitRetryFse);
        }
    }
    /**
     * 上传文件到本地服务器
     * 禁止修改任何逻辑!!!!!!
     *
@@ -315,7 +448,7 @@
        Object value;
        String fileNames;
        FieldSetEntity fieldFse;
        File tempFile;
        File tempFile = null;
        File localTempFile = null;
        long fileLength = 0;
@@ -331,6 +464,7 @@
        }
        String uuids = "";
        String fileFinalName = "";
        for (Map.Entry<Object, Object> entry : map.entrySet()) {
            fieldFse = fieldMetaEntity.getFieldMeta(fieldName);
            if (fieldFse == null || !Arrays.asList(CmnConst.ATTACHMENT_TYPE, "file-image", FieldType.FILE_ATTACHMENT.getDictValue()).contains(fieldFse.getString(CmnConst.FIELD_TYPE))) {
@@ -366,13 +500,11 @@
                }
                boolean saveInDbFlag = FieldType.FILE_ATTACHMENT.getDictValue().equals(fieldFse.getString("field_type"));
                String fileFinalName = "";
                if (!saveInDbFlag) {
                    fileFinalName = FileUtils.uploadFile(tempFile, templateType, clientUuid);
                } else {
                    fileFinalName = System.currentTimeMillis() + RandomUtil.randomString(2);
                }
                ///读取附件时,根据字段类型FileAttachment判断是从目录中还是数据库中获取文件
                // 记录附件信息到数据库
                logger.info("正在记录附件信息到数据库...");
@@ -391,11 +523,9 @@
                if (saveInDbFlag) {
                    // 判断附件字段的类型为FileAttachment时,要把文档内容放到附件表中的file_content字段中
                    try {
                        // todo 6c
//                        attachmentFse.setValue(CmnConst.FILE_CONTENT, new FileInputStream(tempFile));
                        attachmentFse.setValue(CmnConst.FILE_CONTENT, com.product.common.io.FileUtils.readFileToByteArray(tempFile));
                    } catch (Exception e) {
                        throw new RuntimeException("获取文件流失败:666");
                        throw new BaseException(FileCode.FILE_TRANSFER_BYTE_ARRAY_FAIL);
                    }
                }
                //允许编辑
@@ -440,12 +570,16 @@
                    uuids += ",";
                }
                uuids += uuid;
                // 集群布署本地存储时,附件信息要同步到其它服务器上
                if (!saveInDbFlag && !needUpload2FileServerFlag) {
                    File curFile = new File(Global.getSystemConfig("local.dir", "") + File.separator + dir + File.separator + fileFinalName);
                    clusterFileSyncRequest(curFile, fileFinalName, dir + File.separator + fileFinalName, attachmentFse.getUUID());
                }
            }
            logger.info("正在回写uuid...");
            System.out.println(uuids);
            fse.setValue(fieldName, uuids);
            // todo 集群布署时,附件信息要同步到其它服务器上
        }
        return fse;
    }
@@ -718,14 +852,18 @@
                localTempFile.delete();
            }
        } else {
            InputStream is;
            InputStream is = null;
            boolean delete = false;
            File file = null;
            long fileLength = 0;
            if (StringUtils.isEmpty(attachmentFse.getString("attachment_title"))) {
                // todo 6c
                is = new ByteArrayInputStream((byte[]) attachmentFse.getValue("file_content"));
            } else {
            long fileLength = 0L;
            Object fileContent = attachmentFse.getValue("file_content");
            // 是否存储到数据库
            boolean saveInDb = fileContent != null;
            if (saveInDb) {
                is = (InputStream) fileContent;
                fileLength = attachmentFse.getLong("attachment_size");
            }
            if (fileContent == null || needOnlineViewFlag) {
                // 直接在本地的目录中找文件
                logger.info("直接在本地的目录中找文件...");
                String localBasePath = Global.getSystemConfig("local.dir", "");
@@ -744,6 +882,13 @@
                }
                if (needOnlineViewFlag && !file.exists()) {
                    // 若是预览,但是转换后的文件不存在,那么重新执行转换操作
                    // 若是存储到数据库的,那么先查看本地是否存在,若是不存在,那么存一份到本地
                    if (saveInDb) {
                        File localFile = new File(Global.getSystemConfig("local.dir", "") + File.separator + attachmentFse.getString(CmnConst.ATTACHMENT_URL) + File.separator + fileName);
                        if (!localFile.exists()) {
                            Files.copy(is, localFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                        }
                    }
                    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);
@@ -764,15 +909,17 @@
                is = Files.newInputStream(file.toPath());
                fileLength = file.length();
            }
            int len;
            byte[] b = new byte[1024];
            response.setHeader("Access-Control-Expose-Headers", "*");
            response.setContentType("application/octet-stream");
            response.setContentType(contentType);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realFileName, "UTF-8"));
            response.setContentLengthLong(fileLength);
            try (ServletOutputStream os = response.getOutputStream()) {
                while ((len = is.read(b)) > 0) {
                while ((len = is.read(b)) != -1) {
                    if (encrptSignFlag) {
                        // 需要解密
                        logger.info("需要解密...");