Browse Source

onlyoffice 多人编辑

wukai 2 years ago
parent
commit
a0927c6c7d

+ 0 - 34
doc-biz/src/main/java/com/doc/biz/controller/DocInfoController.java

@@ -118,40 +118,6 @@ public class DocInfoController extends BaseController {
     }
 
     /**
-     * 文件下载
-     *
-     * @param fileId fileId
-     * @return
-     */
-    @ApiOperation("文件下载")
-    @GetMapping("/download/{fileId}")
-    public ResponseEntity<Object> download(@PathVariable(name = "fileId") String fileId) {
-        return down(fileId, true);
-    }
-
-    private ResponseEntity<Object> down(String fileId, boolean down) {
-        DocumentVO vo = mongoService.downloadFile(fileId);
-        if (Objects.nonNull(vo)) {
-            String disposition = down ? "attachment;" : "" + "filename=\"" + UriEncoder.encode(vo.getFileName()) + "\"";
-            return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, disposition).header(HttpHeaders.CONTENT_TYPE, vo.getContentType()).header(HttpHeaders.CONTENT_LENGTH, vo.getFileSize() + "").header("Connection", "close").body(vo.getData());
-        } else {
-            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("file does not exist");
-        }
-    }
-
-    /**
-     * 文件下载
-     *
-     * @param fileId fileId
-     * @return
-     */
-    @ApiOperation("文件预览")
-    @GetMapping("/preview/{fileId}")
-    public ResponseEntity<Object> preview(@PathVariable(name = "fileId") String fileId) {
-        return down(fileId, false);
-    }
-
-    /**
      * 文件移动
      */
     @ApiOperation("文件移动")

+ 60 - 34
doc-biz/src/main/java/com/doc/biz/controller/OnlyOfficeController.java

@@ -1,16 +1,29 @@
 package com.doc.biz.controller;
 
 import com.alibaba.fastjson2.JSONObject;
+import com.doc.biz.domain.DocInfo;
+import com.doc.biz.domain.DocVersion;
 import com.doc.biz.service.IDocInfoService;
+import com.doc.biz.service.IDocVersionService;
+import com.doc.biz.service.IMongoService;
+import com.doc.biz.vo.DocumentVO;
+import com.doc.common.core.domain.entity.SysUser;
+import com.doc.common.utils.file.FileUtils;
+import com.doc.system.service.ISysUserService;
+import org.bson.types.Binary;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.io.PrintWriter;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Date;
 import java.util.Scanner;
 
 /**
@@ -24,27 +37,29 @@ import java.util.Scanner;
 public class OnlyOfficeController {
     @Resource
     private IDocInfoService docInfoService;
+    @Resource
+    private IMongoService mongoService;
+    @Resource
+    private IDocVersionService versionService;
+    @Resource
+    private ISysUserService userService;
 
     /**
      * only office修改回调
      *
      * @param id       文档ID
-     * @param name     当前登录的用户名
      * @param request  请求参数
      * @param response 响应参数
-     * @throws Exception
      */
-    @RequestMapping("/callback")
+    @RequestMapping("/callback/{id}")
     @ResponseBody
-    public void save(@RequestParam("id") Long id, @RequestParam("name") String name, HttpServletRequest request, HttpServletResponse response) throws Exception {
+    public void save(@PathVariable("id") Long id, HttpServletRequest request, HttpServletResponse response) throws Exception {
         PrintWriter writer = null;
         String body = "";
-        try {
+        try (Scanner scanner = new Scanner(request.getInputStream());) {
             writer = response.getWriter();
-            Scanner scanner = new Scanner(request.getInputStream());
             scanner.useDelimiter("\\A");
             body = scanner.hasNext() ? scanner.next() : "";
-            scanner.close();
         } catch (Exception ex) {
             writer.write("get request.getInputStream error:" + ex.getMessage());
             return;
@@ -63,34 +78,45 @@ public class OnlyOfficeController {
         4 - 文档已关闭,没有任何更改,
         6 - 正在编辑文档,但保存了当前文档状态,
         7 - 强制保存文档时发生错误。*/
+        int save = 2;
+        int saveError = 3;
+        int forceSave = 6;
         int saved = 0;
         String key = jsonObj.get("key").toString();
-        if (status == 2 || status == 3 || status == 6) {
-            //MustSave, Corrupted
+        if (status == save || status == saveError || status == forceSave) {
+            //获取用户信息
+            SysUser user = userService.selectUserById(jsonObj.getJSONArray("actions").getJSONObject(0).getLong("userid"));
+
             String downloadUri = (String) jsonObj.get("url");
-//            docInfoService.updateFile(id, year, name, downloadUri);
-//            System.err.println(downloadUri);
-//            try {
-//                URL url = new URL(downloadUri);
-//                HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
-//                InputStream stream = connection.getInputStream();
-//                String pathForSave = "D:\\SYSTEM\\Desktop\\temp\\test.docx";
-//                File savedFile = new File(pathForSave);
-//                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();
-//            } catch (Exception ex) {
-//                saved = 1;
-//                ex.printStackTrace();
-//            }
+            DocInfo info = docInfoService.selectDocInfoByDocId(id);
+            info.setRemark(downloadUri);
+            //保存版本信息
+            DocVersion version = new DocVersion();
+            version.setDocId(id);
+            version.setFileId(info.getFileId());
+            version.setCreateBy(info.getCreateBy());
+            version.setCreateTime(info.getCreateTime());
+            versionService.insertDocVersion(version);
+
+            try {
+                URL url = new URL(downloadUri);
+                HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
+
+                MultipartFile multipartFile = FileUtils.getMultipartFile(connection.getInputStream(), info.getFileName());
+                //保存新的文件信息
+                DocumentVO vo = mongoService.uploadFile(multipartFile);
+                info.setFileId(vo.getFileId());
+                info.setFileSize(vo.getFileSize());
+                info.setCreateBy(user.getUserName());
+                info.setCreateTime(new Date());
+                info.setUpdateBy(user.getUserName());
+                docInfoService.updateDocInfo(info);
+
+                connection.disconnect();
+            } catch (Exception ex) {
+                saved = 1;
+                ex.printStackTrace();
+            }
         }
         writer.write("{\"error\":" + saved + "}");
     }

+ 3 - 75
doc-biz/src/main/java/com/doc/biz/domain/DocInfo.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.doc.common.core.domain.BaseEntity;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 
@@ -13,7 +14,8 @@ import org.apache.commons.lang3.builder.ToStringStyle;
  * @author wukai
  * @date 2023-08-21
  */
-@ApiModel(value = "DocInfo" , description = "文件基本信息表")
+@Data
+@ApiModel(value = "DocInfo", description = "文件基本信息表")
 public class DocInfo extends BaseEntity {
     private static final long serialVersionUID = 1L;
 
@@ -59,78 +61,4 @@ public class DocInfo extends BaseEntity {
      */
     @ApiModelProperty("文件类型")
     private String fileType;
-
-    public void setDocId(Long docId) {
-        this.docId = docId;
-    }
-
-    public Long getDocId() {
-        return docId;
-    }
-
-    public void setSpaceId(Long spaceId) {
-        this.spaceId = spaceId;
-    }
-
-    public Long getSpaceId() {
-        return spaceId;
-    }
-
-    public void setDirId(Long dirId) {
-        this.dirId = dirId;
-    }
-
-    public Long getDirId() {
-        return dirId;
-    }
-
-    public void setFileName(String fileName) {
-        this.fileName = fileName;
-    }
-
-    public String getFileName() {
-        return fileName;
-    }
-
-    public void setFileId(String fileId) {
-        this.fileId = fileId;
-    }
-
-    public String getFileId() {
-        return fileId;
-    }
-
-    public void setFileSize(Long fileSize) {
-        this.fileSize = fileSize;
-    }
-
-    public Long getFileSize() {
-        return fileSize;
-    }
-
-    public void setFileType(String fileType) {
-        this.fileType = fileType;
-    }
-
-    public String getFileType() {
-        return fileType;
-    }
-
-    @Override
-    public String toString() {
-        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
-                .append("docId" , getDocId())
-                .append("spaceId" , getSpaceId())
-                .append("dirId" , getDirId())
-                .append("fileName" , getFileName())
-                .append("fileId" , getFileId())
-                .append("fileSize" , getFileSize())
-                .append("fileType" , getFileType())
-                .append("createBy" , getCreateBy())
-                .append("createTime" , getCreateTime())
-                .append("updateBy" , getUpdateBy())
-                .append("updateTime" , getUpdateTime())
-                .append("remark" , getRemark())
-                .toString();
-    }
 }

+ 1 - 0
doc-biz/src/main/java/com/doc/biz/vo/TreeVO.java

@@ -16,5 +16,6 @@ public class TreeVO implements Serializable {
     private Long id;
     private String label;
     private Boolean disabled;
+    private String remark;
     private List<TreeVO> children;
 }

+ 5 - 0
doc-common/pom.xml

@@ -164,6 +164,11 @@
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
         </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>1.5</version>
+        </dependency>
 
     </dependencies>
 

+ 7 - 2
doc-common/src/main/java/com/doc/common/constant/Constants.java

@@ -136,11 +136,16 @@ public class Constants {
     /**
      * 定时任务违规的字符
      */
-    public static final String[] JOB_ERROR_STR = {"java.net.URL" , "javax.naming.InitialContext" , "org.yaml.snakeyaml" ,
-            "org.springframework" , "org.apache" , "com.doc.common.utils.file" , "com.doc.common.config"};
+    public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
+            "org.springframework", "org.apache", "com.doc.common.utils.file", "com.doc.common.config"};
     /**
      * Mongo文件存储方式大小切换
      * 大于该值使用gridFS存储,小于等于该值的直接二进制存储
      */
     public static final long MONGO_FILE_SIZE = 16 * 16 * 1024;
+
+    /**
+     * 图片文件
+     */
+    public static final String IMAGE_EXTENSION = ".bmp.gif.jpg.jpeg.png";
 }

+ 99 - 88
doc-common/src/main/java/com/doc/common/utils/file/FileUtils.java

@@ -4,9 +4,15 @@ import com.doc.common.config.RuoYiConfig;
 import com.doc.common.utils.DateUtils;
 import com.doc.common.utils.StringUtils;
 import com.doc.common.utils.uuid.IdUtils;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileItemFactory;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.http.MediaType;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.commons.CommonsMultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -16,44 +22,35 @@ import java.nio.charset.StandardCharsets;
 
 /**
  * 文件处理工具类
- * 
+ *
  * @author ruoyi
  */
-public class FileUtils
-{
+public class FileUtils {
     public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
 
     /**
      * 输出指定文件的byte数组
-     * 
+     *
      * @param filePath 文件路径
-     * @param os 输出流
+     * @param os       输出流
      * @return
      */
-    public static void writeBytes(String filePath, OutputStream os) throws IOException
-    {
+    public static void writeBytes(String filePath, OutputStream os) throws IOException {
         FileInputStream fis = null;
-        try
-        {
+        try {
             File file = new File(filePath);
-            if (!file.exists())
-            {
+            if (!file.exists()) {
                 throw new FileNotFoundException(filePath);
             }
             fis = new FileInputStream(file);
             byte[] b = new byte[1024];
             int length;
-            while ((length = fis.read(b)) > 0)
-            {
+            while ((length = fis.read(b)) > 0) {
                 os.write(b, 0, length);
             }
-        }
-        catch (IOException e)
-        {
+        } catch (IOException e) {
             throw e;
-        }
-        finally
-        {
+        } finally {
             IOUtils.close(os);
             IOUtils.close(fis);
         }
@@ -66,33 +63,28 @@ public class FileUtils
      * @return 目标文件
      * @throws IOException IO异常
      */
-    public static String writeImportBytes(byte[] data) throws IOException
-    {
+    public static String writeImportBytes(byte[] data) throws IOException {
         return writeBytes(data, RuoYiConfig.getImportPath());
     }
 
     /**
      * 写数据到文件中
      *
-     * @param data 数据
+     * @param data      数据
      * @param uploadDir 目标文件
      * @return 目标文件
      * @throws IOException IO异常
      */
-    public static String writeBytes(byte[] data, String uploadDir) throws IOException
-    {
+    public static String writeBytes(byte[] data, String uploadDir) throws IOException {
         FileOutputStream fos = null;
         String pathName = "";
-        try
-        {
+        try {
             String extension = getFileExtendName(data);
             pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
             File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
             fos = new FileOutputStream(file);
             fos.write(data);
-        }
-        finally
-        {
+        } finally {
             IOUtils.close(fos);
         }
         return FileUploadUtils.getPathFileName(uploadDir, pathName);
@@ -100,17 +92,15 @@ public class FileUtils
 
     /**
      * 删除文件
-     * 
+     *
      * @param filePath 文件
      * @return
      */
-    public static boolean deleteFile(String filePath)
-    {
+    public static boolean deleteFile(String filePath) {
         boolean flag = false;
         File file = new File(filePath);
         // 路径为文件且不为空则进行删除
-        if (file.isFile() && file.exists())
-        {
+        if (file.isFile() && file.exists()) {
             flag = file.delete();
         }
         return flag;
@@ -118,32 +108,28 @@ public class FileUtils
 
     /**
      * 文件名称验证
-     * 
+     *
      * @param filename 文件名称
      * @return true 正常 false 非法
      */
-    public static boolean isValidFilename(String filename)
-    {
+    public static boolean isValidFilename(String filename) {
         return filename.matches(FILENAME_PATTERN);
     }
 
     /**
      * 检查文件是否可下载
-     * 
+     *
      * @param resource 需要下载的文件
      * @return true 正常 false 非法
      */
-    public static boolean checkAllowDownload(String resource)
-    {
+    public static boolean checkAllowDownload(String resource) {
         // 禁止目录上跳级别
-        if (StringUtils.contains(resource, ".."))
-        {
+        if (StringUtils.contains(resource, "..")) {
             return false;
         }
 
         // 检查允许下载的文件规则
-        if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
-        {
+        if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) {
             return true;
         }
 
@@ -153,33 +139,25 @@ public class FileUtils
 
     /**
      * 下载文件名重新编码
-     * 
-     * @param request 请求对象
+     *
+     * @param request  请求对象
      * @param fileName 文件名
      * @return 编码后的文件名
      */
-    public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
-    {
+    public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
         final String agent = request.getHeader("USER-AGENT");
         String filename = fileName;
-        if (agent.contains("MSIE"))
-        {
+        if (agent.contains("MSIE")) {
             // IE浏览器
             filename = URLEncoder.encode(filename, "utf-8");
             filename = filename.replace("+", " ");
-        }
-        else if (agent.contains("Firefox"))
-        {
+        } else if (agent.contains("Firefox")) {
             // 火狐浏览器
             filename = new String(fileName.getBytes(), "ISO8859-1");
-        }
-        else if (agent.contains("Chrome"))
-        {
+        } else if (agent.contains("Chrome")) {
             // google浏览器
             filename = URLEncoder.encode(filename, "utf-8");
-        }
-        else
-        {
+        } else {
             // 其它浏览器
             filename = URLEncoder.encode(filename, "utf-8");
         }
@@ -189,11 +167,10 @@ public class FileUtils
     /**
      * 下载文件名重新编码
      *
-     * @param response 响应对象
+     * @param response     响应对象
      * @param realFileName 真实文件名
      */
-    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
-    {
+    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
         String percentEncodedFileName = percentEncode(realFileName);
 
         StringBuilder contentDispositionValue = new StringBuilder();
@@ -215,36 +192,27 @@ public class FileUtils
      * @param s 需要百分号编码的字符串
      * @return 百分号编码后的字符串
      */
-    public static String percentEncode(String s) throws UnsupportedEncodingException
-    {
+    public static String percentEncode(String s) throws UnsupportedEncodingException {
         String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
         return encode.replaceAll("\\+", "%20");
     }
 
     /**
      * 获取图像后缀
-     * 
+     *
      * @param photoByte 图像数据
      * @return 后缀名
      */
-    public static String getFileExtendName(byte[] photoByte)
-    {
+    public static String getFileExtendName(byte[] photoByte) {
         String strFileExtendName = "jpg";
         if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
-                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
-        {
+                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
             strFileExtendName = "gif";
-        }
-        else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
-        {
+        } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
             strFileExtendName = "jpg";
-        }
-        else if ((photoByte[0] == 66) && (photoByte[1] == 77))
-        {
+        } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
             strFileExtendName = "bmp";
-        }
-        else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
-        {
+        } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
             strFileExtendName = "png";
         }
         return strFileExtendName;
@@ -252,14 +220,12 @@ public class FileUtils
 
     /**
      * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
-     * 
+     *
      * @param fileName 路径名称
      * @return 没有文件路径的名称
      */
-    public static String getName(String fileName)
-    {
-        if (fileName == null)
-        {
+    public static String getName(String fileName) {
+        if (fileName == null) {
             return null;
         }
         int lastUnixPos = fileName.lastIndexOf('/');
@@ -270,17 +236,62 @@ public class FileUtils
 
     /**
      * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
-     * 
+     *
      * @param fileName 路径名称
      * @return 没有文件路径和后缀的名称
      */
-    public static String getNameNotSuffix(String fileName)
-    {
-        if (fileName == null)
-        {
+    public static String getNameNotSuffix(String fileName) {
+        if (fileName == null) {
             return null;
         }
         String baseName = FilenameUtils.getBaseName(fileName);
         return baseName;
     }
+
+    /**
+     * 获取封装得MultipartFile
+     *
+     * @param inputStream inputStream
+     * @param fileName    fileName
+     * @return MultipartFile
+     */
+    public static MultipartFile getMultipartFile(InputStream inputStream, String fileName) {
+        FileItem fileItem = createFileItem(inputStream, fileName);
+        //CommonsMultipartFile是feign对multipartFile的封装,但是要FileItem类对象
+        return new CommonsMultipartFile(fileItem);
+    }
+
+    /**
+     * FileItem类对象创建
+     *
+     * @param inputStream inputStream
+     * @param fileName    fileName
+     * @return FileItem
+     */
+    public static FileItem createFileItem(InputStream inputStream, String fileName) {
+        FileItemFactory factory = new DiskFileItemFactory(16, null);
+        String textFieldName = "file";
+        FileItem item = factory.createItem(textFieldName, MediaType.MULTIPART_FORM_DATA_VALUE, true, fileName);
+        int bytesRead = 0;
+        int len = 8192;
+        byte[] buffer = new byte[len];
+        //使用输出流输出输入流的字节
+        try (OutputStream os = item.getOutputStream()) {
+            while ((bytesRead = inputStream.read(buffer, 0, len)) != -1) {
+                os.write(buffer, 0, bytesRead);
+            }
+            inputStream.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+
+        return item;
+    }
 }

+ 2 - 0
doc-framework/src/main/java/com/doc/framework/config/SecurityConfig.java

@@ -115,6 +115,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 .antMatchers("/login", "/register", "/captchaImage").permitAll()
                 // 增加API接口允许匿名访问
                 .antMatchers("/api/**").permitAll()
+                // 增加API接口允许匿名访问
+                .antMatchers("/only-office/**").permitAll()
                 // 增加websocket允许匿名访问
                 .antMatchers("/websocket/**").permitAll()
                 // 静态资源,可匿名访问