Переглянути джерело

删除服务器本地缓存,预览文件从minIO上获取

陈长荣 8 місяців тому
батько
коміт
376b07847a
26 змінених файлів з 358 додано та 1025 видалено
  1. 4 0
      README.md
  2. 11 4
      pom.xml
  3. 2 0
      src/main/java/com/github/jfcloud/excel/editor/docdeal/DocDealApplication.java
  4. 9 7
      src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/Document.java
  5. 64 38
      src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/DocumentEditCallback.java
  6. 2 1
      src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/DocumentEditCallbackResponse.java
  7. 2 0
      src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/DocumentEditParam.java
  8. 0 14
      src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/File.java
  9. 0 25
      src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/FileHistoryLog.java
  10. 1 4
      src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/FileUpload.java
  11. 0 38
      src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/base/BaseEntity.java
  12. 1 2
      src/main/java/com/github/jfcloud/excel/editor/docdeal/config/LogAspect.java
  13. 4 5
      src/main/java/com/github/jfcloud/excel/editor/docdeal/config/MvcConfiguration.java
  14. 31 0
      src/main/java/com/github/jfcloud/excel/editor/docdeal/constant/CommandEnum.java
  15. 7 4
      src/main/java/com/github/jfcloud/excel/editor/docdeal/constant/DocumentConstants.java
  16. 9 0
      src/main/java/com/github/jfcloud/excel/editor/docdeal/constant/DocumentStatus.java
  17. 0 8
      src/main/java/com/github/jfcloud/excel/editor/docdeal/constant/ErrorCodeEnum.java
  18. 197 225
      src/main/java/com/github/jfcloud/excel/editor/docdeal/controller/FileController.java
  19. 3 3
      src/main/java/com/github/jfcloud/excel/editor/docdeal/exception/WebExceptionHandler.java
  20. 6 2
      src/main/java/com/github/jfcloud/excel/editor/docdeal/oss/service/OssTemplate.java
  21. 0 71
      src/main/java/com/github/jfcloud/excel/editor/docdeal/service/DocumentService.java
  22. 0 306
      src/main/java/com/github/jfcloud/excel/editor/docdeal/service/impl/DocumentServiceImpl.java
  23. 1 2
      src/main/java/com/github/jfcloud/excel/editor/docdeal/service/impl/FileUploadServiceImpl.java
  24. 0 187
      src/main/java/com/github/jfcloud/excel/editor/docdeal/utils/FileUtil.java
  25. 0 74
      src/main/java/com/github/jfcloud/excel/editor/docdeal/utils/Md5Utils.java
  26. 4 5
      src/main/resources/application.yml

+ 4 - 0
README.md

@@ -16,3 +16,7 @@ http://192.168.209.129:20053/web-apps/apps/api/documents/api.js
 http://ip:20054/review/?name=01050625685446bd9597985d94753587.xlsx&userName=zhangjian&userId=1472848120668229634
 http://ip:20054/edit/?name=01050625685446bd9597985d94753587.xlsx&userName=zhangjian&userId=1472848120668229634
 ```
+
+## 接口调用
+
+打开预览接口后,only office先调用Document中的url下载文档,然后调用回调接口

+ 11 - 4
pom.xml

@@ -31,6 +31,10 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
@@ -58,7 +62,13 @@
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
-            <version>1.2.41</version>
+            <version>1.2.83</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-http</artifactId>
+            <version>5.8.28</version>
         </dependency>
 
         <dependency>
@@ -85,12 +95,10 @@
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
-            <version>3.3.2</version>
         </dependency>
         <dependency>
             <groupId>commons-codec</groupId>
             <artifactId>commons-codec</artifactId>
-            <version>1.14</version>
         </dependency>
         <dependency>
             <groupId>org.hashids</groupId>
@@ -100,7 +108,6 @@
         <dependency>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>
-            <version>2.8.5</version>
         </dependency>
         <dependency>
             <groupId>com.googlecode.json-simple</groupId>

+ 2 - 0
src/main/java/com/github/jfcloud/excel/editor/docdeal/DocDealApplication.java

@@ -3,9 +3,11 @@ package com.github.jfcloud.excel.editor.docdeal;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
 
 //启动类
 @SpringBootApplication
+@EnableAspectJAutoProxy
 @MapperScan("com.github.jfcloud.excel.editor.docdeal.mapper")
 public class DocDealApplication {
 

+ 9 - 7
src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/Document.java

@@ -1,6 +1,7 @@
 package com.github.jfcloud.excel.editor.docdeal.bean;
 
 
+import com.alibaba.fastjson.JSONObject;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
@@ -9,6 +10,7 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import java.io.Serializable;
+import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -23,13 +25,13 @@ import java.util.Map;
 @Builder
 public class Document implements Serializable {
     /** 【必需】文件唯一标识 */
-    @ApiModelProperty(value = "文档 key", example="xYz123")
+    @ApiModelProperty(value = "文档 key", required = true)
     private String key;
     /** 【必需】文档名称 */
-    @ApiModelProperty(value = "文档标题", example="test.doc")
+    @ApiModelProperty(value = "文档标题", required = true)
     private String title;
     /** 【必需】文档后缀 */
-    @ApiModelProperty(value = "文档类型", example="doc")
+    @ApiModelProperty(value = "文档类型", required = true)
     private String fileType;
     /** mimeType 应该先校验文件是否可以打开(非api必须字段) */
     //private String mimeType;
@@ -37,14 +39,14 @@ public class Document implements Serializable {
     @ApiModelProperty(value = "文档物理存储位置", example="/temp/test.doc")
     private String storage;
     /** 【必需】文件实体下载地址 */
-    @ApiModelProperty(value = "文档获取url", example="http://192.168.0.58:20053/api/file/xYz123")
+    @ApiModelProperty(value = "文档获取url", required = true)
     private String url;
     /** 打开文件预览/编辑的链接 */
     //private String refrence;
 
-    /** 文档打开方式 {@link OpenModeEnum} */
+    /** 文档打开方式
     //private String mode;
 
-    private Map<String,Object> permissions;
-
+    /** 打开文件预览/编辑的权限 */
+    private JSONObject permissions = new JSONObject();
 }

+ 64 - 38
src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/DocumentEditCallback.java

@@ -15,42 +15,62 @@ import java.util.List;
  * 在文档关闭进行编辑后收到状态 4 ,最后一个用户不做任何更改。
  * 当执行强制保存请求时,接收状态 6 ( 7 )。
  * </p>
+ *
  * @author: zhangcx
  * @date: 2019/8/23 13:48
  */
 @Data
 public class DocumentEditCallback implements Serializable {
-    /** 连接到文档的新用户共同编辑或与其断开连接的操作对象信息 */
+    /**
+     * 连接到文档的新用户共同编辑或与其断开连接的操作对象信息
+     */
     private List<ActionsBean> actions;
-    /** 用于跟踪和显示文档更改历史记录的文档编辑数据 链接 */
+    /**
+     * 用于跟踪和显示文档更改历史记录的文档编辑数据 链接
+     */
     private String changesurl;
-    /** 执行强制保存请求时 启动器的类型 */
+
     /**
-     0 - 对命令服务执行强制保存请求,
-     1 - 每次保存时执行强制保存请求(例如,单击“ 保存”按钮),仅当forcesave选项设置为true时才可用。
-     2 - 使用服务器配置中的设置由计时器执行强制保存请求。
-     当状态值仅等于6或7时,存在类型。
+     * 执行强制保存请求时 启动器的类型,当状态值仅等于6或7时,存在类型。
+     * <ul>
+     *   <li>0 - 对命令服务执行强制保存请求</li>
+     *   <li>1 - 每次保存时执行强制保存请求(例如,单击“ 保存”按钮),仅当forcesave选项设置为true时才可用</li>
+     *   <li>2 - 使用服务器配置中的设置由计时器执行强制保存请求</li>
+     * </ul>
      */
     private int forcesavetype;
-    /** 文档更改历史记录 */
+    /**
+     * 文档更改历史记录
+     */
     private HistoryBean history;
-    /** 【必需】文档key */
+    /**
+     * 【必需】文档key
+     */
     private String key;
     /**
-     *  【必需】文档的状态 {@link DocumentStatus}
-     *  <ul>
-     *  <li>每个用户与文档共同编辑的连接或断开都接收状态 1</li>
-     *  <li>文档关闭后10秒钟收到状态 2 ( 3 ),其中包含最后一次将更改发送到文档编辑服务的用户的id</li>
-     *  <li>最后一个用户不做任何更改,在文档关闭后收到状态 4 </li>
-     *  <li>当执行强制保存请求时,接收状态 6 ( 7 )</li>
-     *  </ul>
+     * 【必需】文档的状态
+     * <ul>
+     *   <li>0 - 找不到具有对应key的文档</li>
+     *   <li>1 - 表示文档正在被编辑</li>
+     *   <li>2 - 表示文档已准备好保存</li>
+     *   <li>3 - 表示文档保存时发生错误</li>
+     *   <li>4 - 表示文档关闭时没有更改</li>
+     *   <li>6 - 表示正在进行强制保存请求</li>
+     *   <li>7 - 表示强制保存文档时发生错误</li>
+     * </ul>
      */
     private int status;
-    /** 文档存储服务中缓存的已编辑的文档的链接 */
+    /**
+     * 文档存储服务中缓存的已编辑的文档的链接
+     */
     private String url;
-    /** 发送到命令服务的自定义信息 */
+    /**
+     * 发送到命令服务的自定义信息
+     */
     private String userdata;
-    /** 同时打开文档进行编辑的用户id列表; 当文档被更改保存时,用户将返回最后一个编辑文档的用户id(状态2和状态6时) */
+    /**
+     * 同时打开文档进行编辑的用户id列表; 当文档被更改保存时,用户将返回最后一个编辑文档的用户id(状态2和状态6时)
+     */
     private List<String> users;
 
     /**
@@ -60,25 +80,27 @@ public class DocumentEditCallback implements Serializable {
     public static class HistoryBean {
         /**
          * 修改记录(json字符串形式)
-         <pre>
-         [{
-                "created": "2019-08-28 05:25:23",
-                "user": {
-                    "name": "用户456",
-                    "id": "456"
-                }
-            }, {
-                "created": "2019-08-28 05:25:33",
-                "user": {
-                    "name": "用户789",
-                    "id": "789"
-                }
-            }
-         ]
-         </pre>
+         * <pre>
+         * [{
+         * "created": "2019-08-28 05:25:23",
+         * "user": {
+         * "name": "用户456",
+         * "id": "456"
+         * }
+         * }, {
+         * "created": "2019-08-28 05:25:33",
+         * "user": {
+         * "name": "用户789",
+         * "id": "789"
+         * }
+         * }
+         * ]
+         * </pre>
          */
         private String changes;
-        /** onlyoffice服务端版本 **/
+        /**
+         * onlyoffice服务端版本
+         **/
         private String serverVersion;
     }
 
@@ -87,9 +109,13 @@ public class DocumentEditCallback implements Serializable {
      */
     @Data
     public static class ActionsBean {
-        /** 1:建立编辑连接 0:断开连接 */
+        /**
+         * 1:建立编辑连接 0:断开连接
+         */
         private int type;
-        /** 共同编辑文档时 连接或断开连接的用户id */
+        /**
+         * 共同编辑文档时 连接或断开连接的用户id
+         */
         private String userid;
     }
 }

+ 2 - 1
src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/DocumentEditCallbackResponse.java

@@ -13,13 +13,14 @@ import java.io.Serializable;
 @Data
 @AllArgsConstructor
 public class DocumentEditCallbackResponse implements Serializable {
+
     private int error;
 
     public static DocumentEditCallbackResponse success(){
         return new DocumentEditCallbackResponse(0);
     }
 
-    public static DocumentEditCallbackResponse failue(){
+    public static DocumentEditCallbackResponse fail(){
         return new DocumentEditCallbackResponse(1);
     }
 }

+ 2 - 0
src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/DocumentEditParam.java

@@ -1,4 +1,5 @@
 package com.github.jfcloud.excel.editor.docdeal.bean;
+import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 
@@ -16,6 +17,7 @@ public class DocumentEditParam {
     private String callbackUrl;
 
     @Data
+    @AllArgsConstructor
     @Builder
     public static class UserBean {
         /** 用户id */

+ 0 - 14
src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/File.java

@@ -1,14 +0,0 @@
-package com.github.jfcloud.excel.editor.docdeal.bean;
-
-import java.io.Serializable;
-
-public class  File implements Serializable {
-    private static final long serialVersionUID = 1L;
-    private String fileId;
-    private String fileName;
-    private String crateTime;
-    private String updateTime;
-    private String createBy;
-    private String updateBy;
-    private String isDel;
-}

+ 0 - 25
src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/FileHistoryLog.java

@@ -1,25 +0,0 @@
-package com.github.jfcloud.excel.editor.docdeal.bean;
-
-
-import com.github.jfcloud.excel.editor.docdeal.bean.base.BaseEntity;
-
-public class FileHistoryLog extends BaseEntity {
-    private static final long serialVersionUID = 1L;
-    private String historyFileId;
-    private String fileId;
-    private String lastSave; //最后保存事件
-    private String notmodified; //最后不修改
-    private String changesUrl;//修改记录zip包路径
-    private String serverVersion; //服务器版本
-    private String changes; //用户修改记录json数组
-    private String actions;//共同编辑用户json数组
-    private String key; //当前版本号
-    private String url; //当前最新记录
-    private String users; //用户数组字符串
-    private String stauts;
-    private String realUrl;  //最新版本文件下载路径
-    private String isDel;
-    private String previousKey;  //之前版本的key
-    private String previousUrl; //之前版本的url
-
-}

+ 1 - 4
src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/FileUpload.java

@@ -5,20 +5,17 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import com.github.jfcloud.excel.editor.docdeal.bean.base.BaseEntity;
 import lombok.Data;
-import lombok.EqualsAndHashCode;
 import lombok.ToString;
 import lombok.experimental.Accessors;
 
 import java.util.Date;
 
 @Data
-@EqualsAndHashCode(callSuper = true)
 @ToString
 @Accessors(chain = true)
 @TableName("onlyoffice_file_info")
-public class FileUpload  extends BaseEntity {
+public class FileUpload {
 
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;

+ 0 - 38
src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/base/BaseEntity.java

@@ -1,38 +0,0 @@
-package com.github.jfcloud.excel.editor.docdeal.bean.base;
-
-import lombok.Getter;
-import lombok.Setter;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-
-/**
- * 基础实体类:用于自动生成数据库表实体的公共字段
- *
- * @author wgb
- * @date 2020/3/26 11:47
- */
-@Getter
-@Setter
-@Accessors(chain = true)
-public class BaseEntity implements Serializable {
-
-
-    /**
-//     * 创建时间,插入数据时自动填充
-//     */
-
-
-//    /**
-//     * 修改时间,插入、更新数据时自动填充
-//     */
-//    @TableField(value = "modified_time", fill = FieldFill.INSERT_UPDATE)
-//    private LocalDateTime modifiedTime;
-//    /**
-//     * 删除状态:插入数据时自动填充
-//     */
-//    @TableField(value = "delete_status", fill = FieldFill.INSERT)
-//    @TableLogic
-//    private boolean deleteStatus;
-
-}

+ 1 - 2
src/main/java/com/github/jfcloud/excel/editor/docdeal/config/LogAspect.java

@@ -31,7 +31,6 @@ import java.util.concurrent.TimeUnit;
 @Component
 @Order(0)
 @Slf4j
-@RequiredArgsConstructor
 public class LogAspect {
 
     /**
@@ -66,7 +65,7 @@ public class LogAspect {
     @AfterReturning(returning = "ret", pointcut = "webLog()")
     public void doAfterReturning(Object ret) {
         // 处理完请求,返回内容
-        log.info("方法的返回值 : " + JSON.toJSONString(ret));
+//        log.info("方法的返回值 : " + JSON.toJSONString(ret));
     }
 
 }

+ 4 - 5
src/main/java/com/github/jfcloud/excel/editor/docdeal/config/MvcConfiguration.java

@@ -13,19 +13,18 @@ import java.util.Map;
 @Configuration
 public class MvcConfiguration implements WebMvcConfigurer {
 
-    @Resource(name="thymeleafViewResolver")
+    @Resource(name = "thymeleafViewResolver")
     private ThymeleafViewResolver thymeleafViewResolver;
     @Value("${files.docservice.url.site}")
     private String documentServerHost;
     @Value("${files.docservice.url.api}")
     private String documentServerApiJs;
+
     @Override
     public void configureViewResolvers(ViewResolverRegistry registry) {
         if (thymeleafViewResolver != null) {
-            Map<String, Object> vars = new HashMap<>(8);
-            vars.put("documentServerApiJs", documentServerHost+documentServerApiJs);
-//            System.out.println( String.format(documentServerApiJs, documentServerHost));
-//            System.out.println( String.format(documentServerApiJs, documentServerHost));
+            Map<String, Object> vars = new HashMap<>(1);
+            vars.put("documentServerApiJs", documentServerHost + documentServerApiJs);
             // 静态参数,只取一次值
             thymeleafViewResolver.setStaticVariables(vars);
         }

+ 31 - 0
src/main/java/com/github/jfcloud/excel/editor/docdeal/constant/CommandEnum.java

@@ -0,0 +1,31 @@
+package com.github.jfcloud.excel.editor.docdeal.constant;
+
+public enum CommandEnum {
+    /**
+     * 强制保存
+     * <a href="https://blog.csdn.net/lz610756247/article/details/143758734">返回值说明</a>
+     */
+    FORCE_SAVE("forcesave"),
+    /**
+     * 获取文档信息
+     */
+    INFO("info"),
+    /**
+     * 更新文档元数据
+     */
+    META("meta"),
+    /**
+     * 文档服务器版本
+     */
+    VERSION("version"),
+    ;
+    private final String command;
+
+    CommandEnum(String command) {
+        this.command = command;
+    }
+
+    public String getCommand() {
+        return command;
+    }
+}

+ 7 - 4
src/main/java/com/github/jfcloud/excel/editor/docdeal/constant/DocumentConstants.java

@@ -1,6 +1,8 @@
 package com.github.jfcloud.excel.editor.docdeal.constant;
 
 import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * @author: zhangcx
@@ -11,17 +13,17 @@ public class DocumentConstants {
     /**
      * 支持的文档类型
      */
-    public static final String[] FILE_TYPE_SUPPORT_VIEW = {"doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "pdf"};
+    public static final List<String> FILE_TYPE_SUPPORT_VIEW = Arrays.asList("doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "pdf");
 
     /**
      * 不支持编辑的类型
      */
-    public static final String[] FILE_TYPE_UNSUPPORT_EDIT = {"pdf"};
+    public static final List<String> FILE_TYPE_UNSUPPORT_EDIT = Arrays.asList("pdf");
 
     /**
      * 文档文件下载接口地址
      */
-    public static final String OFFICE_API_DOC_FILE = "%s/download%s";
+    public static final String OFFICE_API_DOC_FILE = "%s/download?name=%s";
     /**
      * 文档信息获取地址
      */
@@ -29,7 +31,8 @@ public class DocumentConstants {
     /**
      * 编辑回调地址
      */
-    public static final String OFFICE_API_CALLBACK = "%s/callback";
+    public static final String OFFICE_API_CALLBACK = "%s/callback?name=%s";
+    public static final String OFFICE_API_CALLBACK_BUCKET = "%s/callback?name=%s&bucket=%s";
     /**
      * 预览地址
      */

+ 9 - 0
src/main/java/com/github/jfcloud/excel/editor/docdeal/constant/DocumentStatus.java

@@ -45,4 +45,13 @@ public enum DocumentStatus {
                 ", msg='" + msg + '\'' +
                 '}';
     }
+
+    public static DocumentStatus getDocumentStatus(int code) {
+        for (DocumentStatus status : DocumentStatus.values()) {
+            if (status.getCode() == code) {
+                return status;
+            }
+        }
+        return null;
+    }
 }

+ 0 - 8
src/main/java/com/github/jfcloud/excel/editor/docdeal/constant/ErrorCodeEnum.java

@@ -64,12 +64,4 @@ public enum ErrorCodeEnum {
     public boolean isFailed() {
         return !isSuccessful();
     }
-
-    public static void main(String[] args) {
-        System.out.println("| 代码  | 描述   |");
-        System.out.println("| ---- | ---- |");
-        Arrays.stream(ErrorCodeEnum.values()).forEach((ce) -> {
-            System.out.println("| " + StringUtils.rightPad(ce.getCode(), 4) + " | " + ce.getMsg() + " |");
-        });
-    }
 }

+ 197 - 225
src/main/java/com/github/jfcloud/excel/editor/docdeal/controller/FileController.java

@@ -1,33 +1,28 @@
 package com.github.jfcloud.excel.editor.docdeal.controller;
 
 
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.io.file.FileNameUtil;
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.HttpUtil;
 import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.github.jfcloud.excel.editor.docdeal.bean.Document;
-import com.github.jfcloud.excel.editor.docdeal.bean.FileUpload;
+import com.alibaba.fastjson.JSONObject;
+import com.amazonaws.services.s3.model.S3Object;
+import com.github.jfcloud.excel.editor.docdeal.bean.*;
+import com.github.jfcloud.excel.editor.docdeal.constant.CommandEnum;
 import com.github.jfcloud.excel.editor.docdeal.constant.DocumentConstants;
-import com.github.jfcloud.excel.editor.docdeal.constant.ErrorCodeEnum;
-import com.github.jfcloud.excel.editor.docdeal.exception.DocumentException;
+import com.github.jfcloud.excel.editor.docdeal.constant.DocumentStatus;
 import com.github.jfcloud.excel.editor.docdeal.oss.OssProperties;
 import com.github.jfcloud.excel.editor.docdeal.oss.service.OssTemplate;
-import com.github.jfcloud.excel.editor.docdeal.service.DocumentService;
 import com.github.jfcloud.excel.editor.docdeal.service.FileUploadService;
-import com.github.jfcloud.excel.editor.docdeal.utils.FileUtil;
-import com.github.jfcloud.excel.editor.docdeal.utils.Md5Utils;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.hashids.Hashids;
-import org.json.simple.JSONObject;
-import org.json.simple.parser.JSONParser;
-import org.json.simple.parser.ParseException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
-import org.springframework.transaction.annotation.Transactional;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.client.RestTemplate;
@@ -35,290 +30,267 @@ import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.io.*;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Scanner;
+import java.util.List;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Controller
 public class FileController {
-    @Value("${files.savePath}")
-    private String filePath;
-    @Value("${files.docservice.url.site}")
-    private String officeUrl;
-    @Value("${files.docservice.url.command}")
-    private String officeCommand;
-    @Autowired
-    private DocumentService documentService;
-    @Autowired
-    private FileUploadService uploadService;
     @Autowired
     RestTemplate restTemplate;
     @Autowired
     OssTemplate ossTemplate;
     @Autowired
     OssProperties ossProperties;
+    @Value("${files.docservice.url.site}")
+    private String officeUrl;
+    @Value("${files.docservice.url.command}")
+    private String officeCommand;
+    @Value("${document.server.url}")
+    private String serverUrl;
 
-
-    @ResponseBody
-    @GetMapping("/syncOss")
-    @Transactional(rollbackFor = Exception.class)
-    public ResponseEntity<Object> syncOss(@RequestParam(required = false) String bucket,@RequestParam String objectName) {
-        int count = uploadService.count(new LambdaQueryWrapper<FileUpload>()
-                .eq(FileUpload::getFile_name, objectName));
-        if(count>=1){
-            return new ResponseEntity<>(objectName, HttpStatus.OK);
-        }
-        String fileName = FilenameUtils.getName(objectName);
-        if(new File(filePath+fileName).exists()){
-            fileName = FilenameUtils.getBaseName(objectName)+"_"+System.currentTimeMillis()+"."+FilenameUtils.getExtension(objectName);
-        }
-        try(InputStream is = ossTemplate.getObject(StringUtils.isNotBlank(bucket)?bucket:ossProperties.getBucketName(),objectName).getObjectContent();
-            OutputStream ous=new FileOutputStream(filePath+ fileName)) {
-            IOUtils.copy(is,ous);
-            FileUpload upload = new FileUpload();
-            upload.setUpload_date(new Date());
-            upload.setFile_type(fileName.substring(fileName.indexOf(".")));
-            upload.setFile_path(filePath);
-            upload.setFile_name(fileName);
-            upload.setFile_size(0L);
-            uploadService.save(upload);
-            return new ResponseEntity<>(upload, HttpStatus.OK);
-        }catch (Exception e){
-            log.error("文件同步失败",e);
-            throw new RuntimeException();
-        }
-    }
-
-
+    @Autowired
+    private FileUploadService uploadService;
 
     @ResponseBody
     @PostMapping(value = "upload")
-    public ResponseEntity<Object> upload(MultipartFile file, HttpServletRequest request) throws Exception {
+    public String upload(@RequestPart("file") MultipartFile file, @RequestParam(required = false) String bucket) throws Exception {
         if (file.isEmpty()) {
             throw new Exception("上传文件不能为空");
         }
-        FileUpload upload = new FileUpload();
+
         String fileName = file.getOriginalFilename();
-//        if (!fileName.endsWith("xls") && !fileName.endsWith("xlsx")) {
-//            throw new Exception("请上传Excel文件");
-//        }
-        //更新保存文件信息到数据库
-        try(InputStream is = file.getInputStream();OutputStream osm = new FileOutputStream(filePath + file.getOriginalFilename());){
-            FileUtil.saveFile(file.getInputStream(), osm);
+        log.info("上传文件 {}", fileName);
+
+        if (StringUtils.isBlank(bucket)) {
+            bucket = ossProperties.getBucketName();
         }
-//        log.info(fileName);
+        ossTemplate.putObject(bucket, fileName, file.getInputStream());
+        log.info("上传文档已保存,bucket={} name={}", bucket, fileName);
+
+        FileUpload upload = new FileUpload();
         upload.setUpload_date(new Date());
-        log.info("{}",".".indexOf(fileName));
-        log.info("{}",fileName.length());
-        upload.setFile_type(fileName.substring(fileName.indexOf(".")));
-        upload.setFile_path(filePath);
-        upload.setFile_name(file.getOriginalFilename());
+        upload.setFile_type(FileNameUtil.getSuffix(fileName));
+        upload.setFile_path(String.format("/admin/sys-file/%s/%s", bucket, fileName));
+        upload.setFile_name(fileName);
         upload.setFile_size(file.getSize());
         uploadService.save(upload);
-        //操作人
-//        String operator=request.getAttribute(StrUtil.USER_WORKNUMBER).toString();
-//        xxxService.saveUploadCkdExecl(file,operator);
-
-        return new ResponseEntity<Object>("上传成功", HttpStatus.OK);
 
+        return "上传成功";
     }
 
     /**
-     * \
      * 查询所有上传文档信息接口
      *
      * @return
      */
     @GetMapping("/filelist")
-    public ResponseEntity<Object> listFile() {
-
-        return new ResponseEntity<Object>(uploadService.list(), HttpStatus.OK);
+    @ResponseBody
+    public List<FileUpload> listFile() {
+        return uploadService.list();
     }
 
     /**
      * 下载文档接口
+     *
      * @param name
      * @param response
      */
     @GetMapping("/download")
-    public void download(String name, HttpServletResponse response) {
+    public void download(@RequestParam String name,
+                         @RequestParam(required = false) String bucket,
+                         HttpServletResponse response) {
+        if (StrUtil.isBlank(bucket)) {
+            bucket = ossProperties.getBucketName();
+        }
+        log.info("==> 下载文档: name={}, bucket={}", name, bucket);
         try {
-            FileUtil.downLoadFile(name,filePath,response);
+            S3Object s3Object = ossTemplate.getObject(bucket, name);
+            IoUtil.copy(s3Object.getObjectContent(), response.getOutputStream());
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("文档下载失败 name={}, error={}", name, e.getMessage());
         }
     }
 
     @GetMapping("/review")
-    public String reviewDocFile(@RequestParam(required = false) String bucket, @RequestParam(required = false) String title,
-                                @RequestParam String name, String userName, String userId, Model model) {
-        syncOss(bucket, name);
-        String path = filePath + name;
-//        String name = "cc.docx";
-        Document document = documentService.getDocument(documentService.buildDocument(path, name));
-        if (org.springframework.util.StringUtils.hasLength(title)) {
-            document.setTitle(title);
-        }
-        Map<String, Object> map = new HashMap<>();
-        map.put("chat", true);
-        map.put("comment", true);
-        map.put("copy", true);
-        map.put("download", true);
-        map.put("edit", false);
-        map.put("fillForms", false);
-        map.put("modifyContentControl", true);
-        map.put("modifyFilter", true);
-        map.put("print", true);
-        map.put("review", false);
-        map.put("reviewGroups", null);
-        map.put("commentGroups", null);
-        map.put("userInfoGroups", null);
-        document.setPermissions(map);
+    public String reviewDocFile(@RequestParam(required = false) String bucket,
+                                @RequestParam(required = false) String title,
+                                @RequestParam String name,
+                                @RequestParam String userId,
+                                @RequestParam String userName,
+                                Model model) {
+        log.info("==> 预览文档 bucket: {}, name: {}", bucket, name);
+
+        Pair<Document, DocumentEditParam> pair = docPair(bucket, title, name, userId, userName);
+        Document document = pair.getKey();
         model.addAttribute("document", document);
-        // 如果该格式不支持编辑,则返回预览页面
-        if (!documentService.canEdit(document)) {
-            return "/demo";
+        model.addAttribute("documentEditParam", pair.getValue());
+
+        if (document.getFileType().equals("xls") || document.getFileType().equals("xlsx")) {
+            return "/viewerExcel";
         }
-        model.addAttribute("documentEditParam", documentService.buildDocumentEditParam(userId, userName, name));
         return "/viewer";
     }
 
-    @GetMapping("/reviewExcel")
-    public String reviewExcel(@RequestParam(required = false) String bucket, @RequestParam(required = false) String title,
-                              @RequestParam String name, String userName, String userId, Model model) {
-        syncOss(bucket, name);
-        String path = filePath + name;
-//        String name = "cc.docx";
-        Document document = documentService.getDocument(documentService.buildDocument(path, name));
-        if (org.springframework.util.StringUtils.hasLength(title)) {
-            document.setTitle(title);
-        }
-        model.addAttribute("document", document);
-        // 如果该格式不支持编辑,则返回预览页面
-        if (!documentService.canEdit(document)) {
-            return "/demo";
+    private Pair<Document, DocumentEditParam> docPair(String bucket, String title, String name,
+                                                      String userId, String userName) {
+        String downloadUrl = String.format(DocumentConstants.OFFICE_API_DOC_FILE, serverUrl, name);
+        if (StrUtil.isNotBlank(bucket)) {
+            downloadUrl += "&bucket=" + bucket;
         }
-        model.addAttribute("documentEditParam", documentService.buildDocumentEditParam(userId, userName, name));
-        return "/viewerExcel";
+
+        title = StrUtil.isBlank(title) ? name : title;
+        bucket = StrUtil.isBlank(bucket) ? ossProperties.getBucketName() : bucket;
+
+        Document document = Document.builder()
+                .key(FilenameUtils.getBaseName(name))
+                .title(title)
+                .fileType(FilenameUtils.getExtension(name))
+                .url(downloadUrl)
+                .permissions(new JSONObject()
+                        .fluentPut("chat", true)
+                        .fluentPut("comment", true)
+                        .fluentPut("copy", true)
+                        .fluentPut("download", true)
+                        .fluentPut("edit", false)
+                        .fluentPut("fillForms", false)
+                        .fluentPut("modifyContentControl", true)
+                        .fluentPut("modifyFilter", true)
+                        .fluentPut("print", true)
+                        .fluentPut("review", false)
+                        .fluentPut("reviewGroups", null)
+                        .fluentPut("commentGroups", null)
+                        .fluentPut("userInfoGroups", null))
+                .build();
+        log.info("document: {}", JSON.toJSONString(document));
+
+        DocumentEditParam param = DocumentEditParam.builder()
+                .callbackUrl(String.format(DocumentConstants.OFFICE_API_CALLBACK_BUCKET, serverUrl, name, bucket))
+                .user(new DocumentEditParam.UserBean(userId, userName))
+                .build();
+        log.info("param: {}", JSON.toJSONString(param));
+
+        return Pair.of(document, param);
+    }
+
+    @GetMapping("/reviewExcel")
+    public String reviewExcel(@RequestParam(required = false) String bucket,
+                              @RequestParam(required = false) String title,
+                              @RequestParam String name,
+                              String userId,
+                              String userName,
+                              Model model) {
+        return reviewDocFile(bucket, title, name, userName, userId, model);
     }
 
     @GetMapping("/edit")
-    public String editDocFile(@RequestParam(required = false) String bucket, @RequestParam(required = false) String title,
-                              @RequestParam String name, String userName, String userId, Model model) {
-        syncOss(bucket, name);
-        String path = filePath + name;
-//        String name = "cc.docx";
-        Document document = documentService.getDocument(documentService.buildDocument(path, name));
-        if (org.springframework.util.StringUtils.hasLength(title)) {
-            document.setTitle(title);
-        }
+    public String editDocFile(@RequestParam(required = false) String bucket,
+                              @RequestParam(required = false) String title,
+                              @RequestParam String name,
+                              String userName,
+                              String userId,
+                              Model model) {
+        log.info("==> 编辑文档 bucket: {}, name: {}", bucket, name);
+
+        Pair<Document, DocumentEditParam> pair = docPair(bucket, title, name, userId, userName);
+        Document document = pair.getKey();
+        document.getPermissions().clear();
         model.addAttribute("document", document);
-        // 如果该格式不支持编辑,则返回预览页面
-        if (!documentService.canEdit(document)) {
-            return "/demo";
-        }
-        model.addAttribute("documentEditParam", documentService.buildDocumentEditParam(userId, userName, name));
+        model.addAttribute("documentEditParam", pair.getValue());
         return "/editor";
     }
 
 
     /**
-     * 编辑文档时回调接口
-     * @param request
-     * @param response
-     * @throws IOException
+     * Only Office 回调接口
+     * <a href="https://api.onlyoffice.com/zh-CN/docs/docs-api/usage-api/callback-handler/">回调参数</a>
      */
     @RequestMapping("/callback")
-    public void saveDocumentFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        //处理编辑回调逻辑
-        callBack(request, response);
-        String fileName = request.getParameter("fileName");
-        try(InputStream is = new FileInputStream(filePath+ fileName)) {
-            ossTemplate.putObject(ossProperties.getBucketName(),fileName,is);
-            log.info("修改文件{}同步至oss成功",fileName);
-        }catch (Exception e){
-            log.error("文件同步至oss失败",e);
-            throw new RuntimeException();
+    @ResponseBody
+    public DocumentEditCallbackResponse callback(@RequestBody DocumentEditCallback param, HttpServletRequest request) {
+        String name = request.getParameter("name");
+        String bucket = request.getParameter("bucket");
+        log.info("==> onlyoffice 回调 name={}, bucket={}, body={}", name, bucket, JSON.toJSONString(param));
+
+        String userIds = param.getActions()
+                .stream()
+                .map(DocumentEditCallback.ActionsBean::getUserid)
+                .collect(Collectors.joining(", "));
+
+        try {
+            switch (param.getStatus()) {
+                case 0:
+                    log.info("找不到具有对应key的文档 user={}", userIds);
+                    break;
+                case 1:
+                    log.info("文档正在被编辑 user={}", userIds);
+                    break;
+                case 2:
+                case 6:
+                    if (param.getStatus() == 2) {
+                        log.info("文档已准备好保存 user={}", userIds);
+                    } else {
+                        log.info("正在进行强制保存请求 user={}", userIds);
+                    }
+
+                    //更新oss文件
+                    byte[] bytes = HttpUtil.downloadBytes(param.getUrl());
+                    ossTemplate.putObject(bucket, name, new ByteArrayInputStream(bytes));
+                    log.info("文档已保存,bucket={} name={}", bucket, name);
+                    break;
+                case 3:
+                    log.info("文档保存时发生错误 user={}", userIds);
+                    break;
+                case 4:
+                    log.info("文档关闭时没有更改 user={}", userIds);
+                    break;
+                case 7:
+                    log.info("强制保存文档时发生错误 user={}", userIds);
+                    break;
+                default:
+                    log.info("未知状态 user={}", userIds);
+            }
+        } catch (Exception ex) {
+            log.error("文档保存时发生错误 user={} msg={}", userIds, ex.getMessage());
+            return DocumentEditCallbackResponse.fail();
         }
+        return DocumentEditCallbackResponse.success();
     }
 
     /**
+     * 获取文档编辑状态
      *
+     * @param name
      * @return
      */
     @GetMapping("/editStatus")
-    public  ResponseEntity<Object> getDoucmentEditStatus(String name) throws ParseException {
-        String url = officeUrl+officeCommand;
-        Map<String,String>  map = new HashMap<String,String>();
-        map.put("c", "forcesave");
-        String docFileMd5 = Md5Utils.getFileMd5(new File(filePath+name));
-        if (StringUtils.isBlank(docFileMd5)) {
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_MD5_ERROR);
-        }
-        String pathShortMd5 = Md5Utils.md5(filePath + name);
-        String nameShortMd5 = Md5Utils.md5(name);
-        Hashids hashids = new Hashids(DocumentConstants.HASH_KEY);
-        // (将路径字符串短md5值 + 名称字符串短md5值) ==> 再转成短id形式 ==> 作为文档的key(暂且认为是不会重复的)
-        String key = hashids.encodeHex(String.format("%s%s%s", docFileMd5,pathShortMd5, nameShortMd5));
-        map.put("key", key);
-        map.put("userdata", "sample userdata");
-        JSONObject obj = (JSONObject) new JSONParser().parse(FileUtil.editStatus(url, JSON.toJSONString(map)));
-        return new ResponseEntity<Object>(obj, HttpStatus.OK);
+    @ResponseBody
+    public JSONObject getDoucmentEditStatus(@RequestParam String name, @RequestParam(required = false) String command) {
+        log.info("查询文档编辑状态 name={}", name);
 
-    }
-    /**
-     * 处理在线编辑文档的逻辑
-     * @param request
-     * @param response
-     * @throws IOException
-     */
-    private void callBack(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        PrintWriter writer = null;
-        JSONObject jsonObj = null;
-        log.info("===saveeditedfile------------");
+        command = StrUtil.isBlank(command) ? CommandEnum.FORCE_SAVE.getCommand() : command;
+        JSONObject params = new JSONObject()
+                .fluentPut("c", command)
+                .fluentPut("key", name);
 
-        try {
-            writer = response.getWriter();
-            Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
-            String body = scanner.hasNext() ? scanner.next() : "";
-            jsonObj = (JSONObject) new JSONParser().parse(body);
-            log.info("{}",jsonObj);
-            log.info("===saveeditedfile:" + jsonObj.get("status"));
-	            /*
-	                0 - no document with the key identifier could be found,
-	                1 - document is being edited,
-	                2 - document is ready for saving,
-	                3 - document saving error has occurred,
-	                4 - document is closed with no changes,
-	                6 - document is being edited, but the current document state is saved,
-	                7 - error has occurred while force saving the document.
-	             * */
-            if ((long) jsonObj.get("status") == 2) {
-                FileUtil.callBackSaveDocument(jsonObj,filePath,request, response);
-            }
-        } catch (IOException | ParseException e) {
-            e.printStackTrace();
-        }
-        /*
-         * status = 1,我们给onlyoffice的服务返回{"error":"0"}的信息,这样onlyoffice会认为回调接口是没问题的,这样就可以在线编辑文档了,否则的话会弹出窗口说明
-         * 在线编辑还没有关闭,前端有人下载文档时,强制保存最新内容  当status 是6时说明有人在编辑时下载文档
-         * */
-        log.info("{}",jsonObj.get("status"));
-        if ((long) jsonObj.get("status") == 6) {
-            //处理当文档正在编辑为关闭时,下载文档
-            if (((String)jsonObj.get("userdata")).equals("sample userdata")){
-                FileUtil.callBackSaveDocument(jsonObj,filePath,request, response);
+        try (HttpResponse response = HttpUtil.createPost( officeUrl + officeCommand)
+                .body(params.toJSONString())
+                .header("Content-Type", "application/json")
+                .execute()) {
+
+            JSONObject responseObj = JSON.parseObject(response.body());
+            if (CommandEnum.INFO.getCommand().equals(command)) {
+                DocumentStatus documentStatus = DocumentStatus.getDocumentStatus(responseObj.getInteger("error"));
+                if (documentStatus != null) {
+                    responseObj.put("msg", documentStatus.getMsg());
+                }
             }
 
-            log.info("====保存失败:");
-            writer.write("{\"error\":1}");
-        } else {
-            //执行删除编辑时下载保存的文件:
-            FileUtil.deleteTempFile(filePath,request.getParameter("fileName"));
-            writer.write("{\"error\":0}");
+            log.info("文档({})编辑状态 {}", name, responseObj);
+            return responseObj;
         }
     }
 

+ 3 - 3
src/main/java/com/github/jfcloud/excel/editor/docdeal/exception/WebExceptionHandler.java

@@ -14,19 +14,19 @@ import org.springframework.web.bind.annotation.ResponseBody;
 @Slf4j
 public class WebExceptionHandler {
     @ExceptionHandler(DocumentException.class)
-    public DocumentResponse documentException(DocumentException e) {
+    public DocumentResponse<?> documentException(DocumentException e) {
         log.error("$$$ 文档异常~~", e);
         return DocumentResponse.failue(e.getErrorCode());
    }
 
     @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
-    public DocumentResponse requestException(HttpRequestMethodNotSupportedException e) {
+    public DocumentResponse<?> requestException(HttpRequestMethodNotSupportedException e) {
         log.error("$$$ 不支持的请求类型~~", e);
         return DocumentResponse.failue(ErrorCodeEnum.UNSUPPORTED_REQUEST_METHOD);
     }
 
     @ExceptionHandler(Exception.class)
-    public DocumentResponse unknownException(Exception e) {
+    public DocumentResponse<?> unknownException(Exception e) {
         log.error("$$$ 未知异常~~", e);
         return DocumentResponse.failue(ErrorCodeEnum.SYSTEM_UNKNOWN_ERROR);
     }

+ 6 - 2
src/main/java/com/github/jfcloud/excel/editor/docdeal/oss/service/OssTemplate.java

@@ -103,8 +103,12 @@ public class OssTemplate implements InitializingBean {
         ObjectMetadata objectMetadata = new ObjectMetadata();
         objectMetadata.setContentLength(size);
         objectMetadata.setContentType(contextType);
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
-        return this.amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata);
+
+        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)) {
+            return this.amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata);
+        } finally {
+            stream.close();
+        }
     }
 
     public S3Object getObjectInfo(String bucketName, String objectName) throws Exception {

+ 0 - 71
src/main/java/com/github/jfcloud/excel/editor/docdeal/service/DocumentService.java

@@ -1,71 +0,0 @@
-package com.github.jfcloud.excel.editor.docdeal.service;
-
-
-
-import com.github.jfcloud.excel.editor.docdeal.bean.Document;
-import com.github.jfcloud.excel.editor.docdeal.bean.DocumentEditParam;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * 文档业务 接口
- * @author: zhangcx
- * @date: 2019/8/7 16:30
- */
-public interface DocumentService {
-    /**
-     * 构建文档对象
-     * @param filePath
-     * @param fileName
-     * @return documentKey 文档key
-     */
-    String buildDocument(String filePath, String fileName);
-
-    /**
-     * 从缓从中获取文档信息
-     * @param documentKey
-     * @return
-     */
-    Document getDocument(String documentKey);
-
-    /**
-     * 下载文档实体文件
-     * @param documentKey
-     * @param request
-     * @param response
-     * @throws IOException
-     */
-    void downloadDocumentFile(String documentKey, HttpServletRequest request, HttpServletResponse response) throws IOException;
-
-    /**
-     * 构建文档编辑参数 对象
-     *
-     * @param userId
-     * @param userName
-     * @return
-     */
-    DocumentEditParam buildDocumentEditParam(String userId, String userName, String fileName);
-
-    /**
-     * 编辑后保存文档实体文件
-     * @param documentKey
-     * @param downloadUrl
-     * @throws IOException
-     */
-    boolean saveDocumentFile(String documentKey, String downloadUrl) throws IOException;
-
-    /**
-     * 获取服务暴露的host(包含端口)
-     * @return
-     */
-    Object getServerHost();
-
-    /**
-     * 文档是否支持编辑
-     * @param document
-     * @return
-     */
-    boolean canEdit(Document document);
-}

+ 0 - 306
src/main/java/com/github/jfcloud/excel/editor/docdeal/service/impl/DocumentServiceImpl.java

@@ -1,306 +0,0 @@
-package com.github.jfcloud.excel.editor.docdeal.service.impl;
-
-import com.alibaba.fastjson.JSON;
-import com.github.jfcloud.excel.editor.docdeal.bean.Document;
-import com.github.jfcloud.excel.editor.docdeal.bean.DocumentEditParam;
-import com.github.jfcloud.excel.editor.docdeal.constant.DocumentConstants;
-import com.github.jfcloud.excel.editor.docdeal.constant.ErrorCodeEnum;
-import com.github.jfcloud.excel.editor.docdeal.exception.DocumentException;
-import com.github.jfcloud.excel.editor.docdeal.service.DocumentService;
-import com.github.jfcloud.excel.editor.docdeal.utils.Md5Utils;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.hashids.Hashids;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.env.Environment;
-import org.springframework.stereotype.Service;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.*;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-
-/**
- * 文档相关业务方法
- *
- * @author: zhangcx
- * @date: @date: 2019/8/7 16:30
- */
-@Slf4j
-@Service
-public class DocumentServiceImpl implements DocumentService {
-    @Autowired
-    Environment environment;
-    @Value("${document.server.host}")
-    private String serverHost ;
-    /**
-     * 大小限制,默认10M,单位kb
-     */
-    @Value("${document.file-size.limit:1048576000}")
-    private Long docFileSizeLimit;
-    @Value("${files.docservice.url.site}")
-    private String documentServerHost;
-    @Value("${files.docservice.url.api}")
-    private String documentServerApiJs;
-   // @Autowired
-    //private DocumentCacheService cacheService;
-
-    @Override
-    public String buildDocument(String filePath, String fileName) {
-        if (StringUtils.isBlank(filePath)) {
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_NOT_EXISTS);
-        }
-        filePath = FilenameUtils.normalize(filePath);
-        String fileType = StringUtils.lowerCase(FilenameUtils.getExtension(filePath));
-        if (StringUtils.isBlank(fileType)) {
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_NO_EXTENSION);
-        }
-        // 如果指定了文件名,则需要校验和实体文件格式是否一致
-        if (StringUtils.isNotBlank(fileName) && !fileType.equalsIgnoreCase(FilenameUtils.getExtension(fileName))) {
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_EXTENSION_NOT_MATCH);
-        }
-        File docFile = new File(filePath);
-        // 校验文件实体
-        preFileCheck(docFile);
-        fileName = StringUtils.isNotBlank(fileName) ? fileName : docFile.getName();
-        String fileKey = this.fileKey(docFile, fileName);
-        Document document = Document.builder()
-                                .fileType(fileType)
-                                .title(fileName)
-                                .storage(filePath)
-                                .build();
-        boolean cached = false;
-//        try {
-//            cached = cacheService.put(fileKey, document);
-//        } catch (Exception e) {
-//            log.error("$$$ 缓存失败~~", e);
-//        }
-//        if (!cached) {
-//            throw new DocumentException(ErrorCodeEnum.DOC_CACHE_ERROR);
-//        }
-        document.setKey(fileKey);
-        return JSON.toJSONString(document);
-    }
-
-    @Override
-    public Document getDocument(String documentKey) {
-        Document doc = null;
-
-        try {
-            doc = JSON.parseObject(documentKey,Document.class);
-        } catch (Exception e) {
-            log.error("$$$ 获取缓存失败~~", e);
-        }
-        if (doc == null) {
-            throw new DocumentException(ErrorCodeEnum.DOC_CACHE_NOT_EXISTS);
-        }
-        // 从缓存中取出后,再绑定非必需缓存字段(节省缓存大小)
-//        doc.setKey(documentKey);
-        doc.setUrl(fileUrl(doc.getTitle()));
-        if (log.isDebugEnabled()) {
-            log.info(doc.toString());
-        }
-        log.info(doc.toString());
-        return doc;
-    }
-
-    /**
-     * 计算文件key值: 文件md5值+路径的短md5值+名称的短md5值
-     * @param docFile
-     * @param name 生成协作时文档的docuemnt.key的值
-     * @return
-     */
-    public String fileKey(File docFile, String name) {
-        String docFileMd5 = Md5Utils.getFileMd5(docFile);
-        if (StringUtils.isBlank(docFileMd5)) {
-            log.error("$$$ 构建文件信息失败!计算文件 md5 失败!");
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_MD5_ERROR);
-        }
-        String pathShortMd5 = Md5Utils.md5(docFile.getAbsolutePath());
-        String nameShortMd5 = Md5Utils.md5(name);
-        Hashids hashids = new Hashids(DocumentConstants.HASH_KEY);
-        // (将路径字符串短md5值 + 名称字符串短md5值) ==> 再转成短id形式 ==> 作为文档的key(暂且认为是不会重复的)
-        String key = hashids.encodeHex(String.format("%s%s%s", docFileMd5,pathShortMd5, nameShortMd5));
-        if (StringUtils.isBlank(key)) {
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_KEY_ERROR);
-        }
-        return key;
-    }
-
-    /**
-     * 文件key值
-     * @param fileType
-     * @param docCrc32
-     * @return
-     */
-    private String fileKey(String fileType, String docCrc32) {
-        return String.format("%s_%s", fileType, docCrc32);
-    }
-
-    /**
-     * 文件url地址
-     * @param
-     * @return
-     */
-    private String fileUrl(String filename) {
-        return String.format(DocumentConstants.OFFICE_API_DOC_FILE, getServerHost(), "?name="+filename);
-//        return  "http://192.168.0.58:20053/download?name="+filename;
-    }
-
-    /**
-     * 根据文档信息下载文档文件
-     * @param documentKey
-     * @param request
-     * @param response
-     * @throws IOException
-     */
-    @Override
-    public void downloadDocumentFile(String documentKey, HttpServletRequest request, HttpServletResponse response) throws IOException {
-        Document doc = this.getDocument(documentKey);
-        File file = new File(doc.getStorage());
-        try (InputStream reader = new FileInputStream(file);
-             OutputStream out = response.getOutputStream()) {
-            byte[] buf = new byte[(int) FileUtils.ONE_KB];
-            int len = 0;
-            //response.setContentType(mimeType(file));
-            while ((len = reader.read(buf)) != -1) {
-                out.write(buf, 0, len);
-            }
-            out.flush();
-        } catch (Exception e) {
-            log.error("下载失败!读取文件[" + doc.getStorage() + "]报错~~", e);
-        }
-    }
-
-    @Override
-    public DocumentEditParam buildDocumentEditParam(String userId, String userName, String fileName) {
-        return DocumentEditParam.builder()
-                .callbackUrl(callbackUrl(fileName))
-                .user(DocumentEditParam.UserBean.builder()
-                        .id(userId)
-                        .name(userName)
-                        .build())
-                .build();
-    }
-
-    private String callbackUrl(String fileName) {
-        String format = String.format(DocumentConstants.OFFICE_API_CALLBACK, getServerHost());
-        format=format+"?fileName="+fileName;
-        return format;
-    }
-
-    /**
-     * 上传文档实体文件
-     * @param documentKey
-     * @param downloadUrl
-     * @throws IOException
-     */
-    @Override
-    public boolean saveDocumentFile(String documentKey, String downloadUrl) {
-        if (log.isInfoEnabled()) {
-            log.info(downloadUrl);
-        }
-        // TODO 默认覆盖源文件,如果调用者指定,则存到临时目录?
-        boolean isCover = true;
-        Document doc = this.getDocument(documentKey);
-        String saveFilePath = doc.getStorage();
-//        if (!isCover) {
-//            String baseDir = environment.getProperty("java.io.tmpdir");
-//            saveFilePath = String.format("%s/office-api/%s/%s.%s", baseDir, documentKey, System.currentTimeMillis(), doc.getFileType());
-//        }
-        File saveFile = new File(saveFilePath);
-        boolean success = false;
-        try {
-            FileUtils.copyURLToFile(new URL(downloadUrl), saveFile);
-            if (saveFile.exists() && saveFile.length() > 0) {
-                success = true;
-            }
-        } catch (IOException e) {
-            log.error("$$$ 保存文档失败!", e);
-        }
-        return success;
-        //TODO 编辑成功后,应该删除之前的编辑状态缓存
-    }
-
-    @Override
-    public Object getServerHost() {
-        if (StringUtils.startsWith(serverHost, DocumentConstants.HTTP_SCHEME)) {
-            return serverHost;
-        }
-        return String.format("http://%s", serverHost);
-    }
-
-    @Override
-    public boolean canEdit(Document document) {
-        if (ArrayUtils.contains(DocumentConstants.FILE_TYPE_UNSUPPORT_EDIT, document.getFileType())) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * 获取文档信息api地址
-     * @param docId
-     * @return
-     */
-    private String docInfoUrl(String docId) {
-        return String.format(DocumentConstants.OFFICE_API_DOC, getServerHost(), docId);
-    }
-
-    /**
-     * 获取文件的 mimetype
-     * @param file
-     * @deprecated
-     * @return
-     */
-    @Deprecated
-    private String mimeType(File file) {
-        try {
-            return Files.probeContentType(Paths.get(file.toURI()));
-        } catch (IOException e) {
-            log.error("$$$ 获取文件mimeType错误!", e);
-        }
-        return null;
-    }
-
-    /**
-     * 先校验文档文件
-     * @param docFile
-     * @return
-     */
-    private void preFileCheck(File docFile) {
-        if (log.isDebugEnabled()) {
-            log.debug("### 开始校验文档:[{}]", docFile.getAbsolutePath());
-        }
-        if (docFile == null || !docFile.exists()) {
-            log.error("$$$ 目标文档不存在,无法打开!");
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_NOT_EXISTS);
-        }
-        if (docFile.isDirectory() || docFile.length() <= 0) {
-            log.error("$$$ 目标文档[{}]是目录或空文件,无法打开!", docFile.getAbsolutePath());
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_EMPTY);
-        }
-        if (!docFile.canRead()) {
-            log.error("$$$ 目标文档[{}]不可读,无法打开!", docFile.getAbsolutePath());
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_UNREADABLE);
-        }
-        if (docFile.length() > docFileSizeLimit) {
-            log.error("$$$ 目标文档大小超过限制({}B > {}B),无法打开!", docFile.length(), docFileSizeLimit);
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_OVERSIZE);
-        }
-        String ext = StringUtils.lowerCase(FilenameUtils.getExtension(docFile.getName()));
-        if (!ArrayUtils.contains(DocumentConstants.FILE_TYPE_SUPPORT_VIEW, ext)) {
-            log.error("$$$ 目标文档格式[{}]不正确,无法打开!(只支持:{})",
-                    ext, StringUtils.join(DocumentConstants.FILE_TYPE_SUPPORT_VIEW, ","));
-            throw new DocumentException(ErrorCodeEnum.DOC_FILE_TYPE_UNSUPPORTED);
-        }
-    }
-
-
-}

+ 1 - 2
src/main/java/com/github/jfcloud/excel/editor/docdeal/service/impl/FileUploadServiceImpl.java

@@ -12,12 +12,11 @@ import java.util.List;
 @Service
 public class FileUploadServiceImpl extends ServiceImpl<FileUploadMapper, FileUpload> implements FileUploadService {
     @Autowired
-    FileUploadMapper  uploadMapper;
+    FileUploadMapper uploadMapper;
 
     @Override
     public List<FileUpload> list() {
         return uploadMapper.selectList(null);
     }
 
-
 }

+ 0 - 187
src/main/java/com/github/jfcloud/excel/editor/docdeal/utils/FileUtil.java

@@ -1,187 +0,0 @@
-package com.github.jfcloud.excel.editor.docdeal.utils;
-
-import org.apache.commons.io.IOUtils;
-import org.json.simple.JSONObject;
-import org.springframework.stereotype.Component;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.*;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLEncoder;
-
-public class FileUtil {
-    /**
-     * 保存文件方法
-     * @param in
-     * @throws Exception
-     */
-    public static void saveFile(InputStream in, OutputStream osm) throws Exception {
-//        FileChannel in = new FileInputStream("src/demo20/data.txt").getChannel(),
-//                out = new FileOutputStream("src/demo20/data2.txt").getChannel();
-//        in.transferTo(0, in.size(), out);
-
-        IOUtils.copy(in, osm);
-
-    }
-
-
-
-    /**
-     * 前端下载文件方法
-     * @param name
-     * @param filePath
-     * @param response
-     * @throws IOException
-     */
-    public static void downLoadFile(String name, String filePath, HttpServletResponse response) throws IOException {
-
-        String path = filePath + name;
-        // path是指想要下载的文件的路径
-        File file = new File(path);
-        //log.info(file.getPath());
-        // 获取文件名
-        String filename = file.getName();
-        // 获取文件后缀名
-        String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
-        System.out.println("文件后缀名:" + ext);
-
-        // 将文件写入输入流
-        FileInputStream fileInputStream = new FileInputStream(file);
-        InputStream fis = new BufferedInputStream(fileInputStream);
-        byte[] buffer = new byte[fis.available()];
-        fis.read(buffer);
-        fis.close();
-
-        // 清空response
-        response.reset();
-        // 设置response的Header
-        response.setCharacterEncoding("UTF-8");
-        //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
-        //attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"
-        // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
-        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
-        // 告知浏览器文件的大小
-        response.addHeader("Content-Length", "" + file.length());
-        OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
-        response.setContentType("application/octet-stream");
-        outputStream.write(buffer);
-        outputStream.flush();
-    }
-
-    /**
-     * 编辑以后保存文件
-     * @param jsonObj
-     * @param filePath
-     * @param request
-     * @param response
-     * @throws IOException
-     */
-    public static void callBackSaveDocument(JSONObject jsonObj, String filePath, HttpServletRequest request, HttpServletResponse response) throws IOException {
-        /*
-         * 当我们关闭编辑窗口后,十秒钟左右onlyoffice会将它存储的我们的编辑后的文件,,此时status = 2,通过request发给我们,我们需要做的就是接收到文件然后回写该文件。
-         * */
-        /*
-         * 定义要与文档存储服务保存的编辑文档的链接。当状态值仅等于2或3时,存在链路。
-         * */
-        String downloadUri = (String) jsonObj.get("url");
-        System.out.println("====文档编辑完成,现在开始保存编辑后的文档,其下载地址为:" + downloadUri);
-        //解析得出文件名
-        //String fileName = downloadUri.substring(downloadUri.lastIndexOf('/')+1);
-        String fileName = request.getParameter("fileName");
-        System.out.println("====下载的文件名:" + fileName);
-
-        URL url = new URL(downloadUri);
-        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-        InputStream stream = connection.getInputStream();
-        //更换为实际的路径F:\DataOfHongQuanzheng\java\eclipse-workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\Java Example\\app_data\192.168.56.1\
-        //File savedFile = new File("F:\\DataOfHongQuanzheng\\onlyoffice_data\\app_data\\"+fileName);
-        File savedFile = new File(filePath + fileName);
-        if (null!=((String) jsonObj.get("userdata"))&&((String) jsonObj.get("userdata")).equals("sample userdata")) {
-            savedFile = new File(filePath + "v1" + fileName);
-        }
-
-
-        try (FileOutputStream out = new FileOutputStream(savedFile)) {
-            int read;
-            final byte[] bytes = new byte[1024];
-            while ((read = stream.read(bytes)) != -1) {
-                out.write(bytes, 0, read);
-            }
-            out.flush();
-        }
-        connection.disconnect();
-    }
-
-    /**
-     * 发送网路请求查看是否正在编辑
-     * @param path
-     * @param params
-     * @return
-     */
-    public static String editStatus(String path, String params) {
-        OutputStreamWriter out = null;
-        BufferedReader in = null;
-        StringBuilder result = new StringBuilder();
-        HttpURLConnection conn = null;
-        try {
-            URL url = new URL(path);
-            conn = (HttpURLConnection) url.openConnection();
-            conn.setRequestMethod("POST");
-            //发送POST请求必须设置为true
-            conn.setDoOutput(true);
-            conn.setDoInput(true);
-            //设置连接超时时间和读取超时时间
-            conn.setConnectTimeout(30000);
-            conn.setReadTimeout(10000);
-            conn.setRequestProperty("Content-Type", "application/json");
-            conn.setRequestProperty("Accept", "application/json");
-            //获取输出流
-            out = new OutputStreamWriter(conn.getOutputStream());
-//            String jsonStr = "{\"c\":\"forcesave\", \"key\":\"WpP7m85eNQSEOoepp31oIYVG2oJyJJcvkLdoywgvs1k3ywm3Omuxk4\",\"userdata\":\"sample userdata\"}";
-            out.write(params);
-            out.flush();
-            out.close();
-            //取得输入流,并使用Reader读取
-            if (200 == conn.getResponseCode()) {
-
-                return IOUtils.toString(conn.getInputStream());
-
-
-            } else {
-                System.out.println("ResponseCode is an error code:" + conn.getResponseCode());
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        } finally {
-            try {
-                if (out != null) {
-                    out.close();
-                }
-                if (in != null) {
-                    in.close();
-                }
-            } catch (IOException ioe) {
-                ioe.printStackTrace();
-            }
-        }
-
-        return "";
-    }
-
-    /**
-     * 当最后关闭编辑界面后,将编辑时下载的文件删除
-     *
-     * @param path
-     * @param fileName
-     */
-    public static void deleteTempFile(String path, String fileName) {
-        //因为临时存储的文件都添加了v1前缀所以删除文件时需要在文件名测前边加一个v1
-        File file = new File(path + "v1" + fileName);
-        if (file.exists()) {
-            file.delete();
-        }
-
-    }
-}

+ 0 - 74
src/main/java/com/github/jfcloud/excel/editor/docdeal/utils/Md5Utils.java

@@ -1,74 +0,0 @@
-package com.github.jfcloud.excel.editor.docdeal.utils;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.lang3.StringUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * Md5 工具类
- * need  commons-codec-1.6.jar +
- * @author zhangcx
- * @date 2019-7-23
- */
-@Slf4j
-public class Md5Utils {
-    private static MessageDigest MD5 = null;
-
-    static {
-        try {
-            MD5 = MessageDigest.getInstance("MD5");
-        } catch (NoSuchAlgorithmException ne) {
-            ne.printStackTrace();
-        }
-    }
-
-    public static String getFileMd5(String filePath) {
-        if (StringUtils.isBlank(filePath)) {
-            return null;
-        }
-        return getFileMd5(new File(filePath));
-    }
-
-    /**
-     * 对一个文件获取md5值
-     */
-    public static String getFileMd5(File file) {
-        FileInputStream fileInputStream = null;
-        try {
-            fileInputStream = new FileInputStream(file);
-            byte[] buffer = new byte[8192];
-            int length;
-            while ((length = fileInputStream.read(buffer)) != -1) {
-                MD5.update(buffer, 0, length);
-            }
-            return new String(Hex.encodeHex(MD5.digest()));
-        } catch (IOException e) {
-            log.error("$$$ 获取文件md5失败!", e);
-            return null;
-        } finally {
-            try {
-                if (fileInputStream != null) {
-                    fileInputStream.close();
-                }
-            } catch (IOException e) {
-                log.error("关闭文件流出错!", e);
-            }
-        }
-    }
-
-    /**
-     * 计算字符串的md5值
-     * @param target 字符串
-     * @return md5 value
-     */
-    public static String md5(final String target) {
-        return DigestUtils.md5Hex(target);
-    }
-}

+ 4 - 5
src/main/resources/application.yml

@@ -27,7 +27,7 @@ server:
 #  bucket-name: report
 
 oss:
-  endpoint: http://192.168.66.228:9002
+  endpoint: http://192.168.66.227:9002
   access-key: jfcloud
   secret-key: jfcloudjfcloud
   bucket-name: report
@@ -82,17 +82,16 @@ pagehelper:
 
 document:
   server:
-    host: 192.168.66.228:20054
+    url: http://192.168.66.228:${server.port} # 当前程序的服务器地址
 files:
-  savePath: onlyoffice/
-  #savePath: D:\onlyoffice\
+  #savePath: onlyoffice/
   docservice:
     secret: cThavmwXYSEExEtM54hBpqu0vggVv7MP
     convert-docs: .docm|.dotx|.dotm|.dot|.doc|.odt|.fodt|.ott|.xlsm|.xltx|.xltm|.xlt|.xls|.ods|.fods|.ots|.pptm|.ppt|.ppsx|.ppsm|.pps|.potx|.potm|.pot|.odp|.fodp|.otp|.rtf|.mht|.html|.htm|.xml|.epub|.fb2
     edited-docs: .docx|.xlsx|.csv|.pptx|.txtls
     viewed-docs: .pdf|.djvu|.xps
     url:
-      site: http://192.168.66.228:20053/
+      site: http://192.168.66.227:20053/ # onlyOffice 服务器地址
       converter: ConvertService.ashx
       command: coauthoring/CommandService.ashx
       api: web-apps/apps/api/documents/api.js