提交 f3739005 作者: ZhangJingKun

在线编辑 zhangjingkun

上级 a25d5ae9
......@@ -160,6 +160,18 @@
<artifactId>htmlcleaner</artifactId>
<version>2.25</version>
</dependency>
<dependency>
<groupId>org.hashids</groupId>
<artifactId>hashids</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
<build>
......
package com.zzsn.knowbase.constant;
import java.time.Duration;
/**
* @author: zhangcx
* @date: 2019/8/7 17:23
*/
public class DocumentConstants {
public static final String HTTP_SCHEME = "http";
/**
* 支持的文档类型
*/
public static final String[] FILE_TYPE_SUPPORT_VIEW = {"doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "pdf"};
/**
* 不支持编辑的类型
*/
public static final String[] FILE_TYPE_UNSUPPORT_EDIT = {"pdf"};
/**
* 文档文件下载接口地址
*/
public static final String OFFICE_API_DOC_FILE = "%s/download%s";
/**
* 文档信息获取地址
*/
public static final String OFFICE_API_DOC = "%s/api/doc/%s";
/**
* 编辑回调地址
*/
public static final String OFFICE_API_CALLBACK = "%s/callback";
/**
* 预览地址
*/
public static final String OFFICE_VIEWER = "%s/viewer/%s";
/**
* 编辑地址
*/
public static final String OFFICE_EDITOR = "%s/editor/%s";
/**
* 文档redis缓存前缀 格式化
*/
public static final String DOCUMENT_REDIS_KEY_PREFIX_FORMAT = "onlyoffice:document:%s";
/**
* 缓存过期时间: 1天
*/
public static final Duration CACHE_DURATION = Duration.ofDays(1);
public static final String HASH_KEY = "lezhixing";
}
......@@ -16,7 +16,7 @@
*
*/
package com.zzsn.knowbase.enums;
package com.zzsn.knowbase.constant;
public enum DocumentType {
word,
......
package com.zzsn.knowbase.constant;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
/**
* 错误码枚举
* 约定 code 为 0 表示操作成功,
* 1 或 2 等正数表示软件错误,
* -1, -2 等负数表示系统错误.
* @author: zhangcx
* @date: 2019/8/12 13:18
*/
public enum ErrorCodeEnum {
SUCCESS("0", "success"),
DOC_FILE_NOT_EXISTS("1001", "目标文档不存在"),
DOC_FILE_EMPTY("1002", "目标文档是目录或空文件"),
DOC_FILE_UNREADABLE("1003", "目标文档不可读"),
DOC_FILE_OVERSIZE("1004", "目标文档大小超过限制"),
DOC_FILE_TYPE_UNSUPPORTED("1005", "目标文档格式不正确"),
DOC_FILE_MD5_ERROR("1006", "目标文档md5校验失败"),
DOC_FILE_MIME_ERROR("1007", "目标文档MIME检查失败"),
DOC_FILE_NO_EXTENSION("1008", "文件路径不包含扩展名"),
DOC_FILE_EXTENSION_NOT_MATCH("1009", "文件路径和名称后缀不匹配"),
DOC_FILE_KEY_ERROR("1010", "目标文档key计算失败"),
DOC_CACHE_ERROR("1101", "文档信息缓存失败"),
DOC_CACHE_NOT_EXISTS("1102", "从缓存中获取文档信息失败"),
UNSUPPORTED_REQUEST_METHOD("1201", "不支持的请求类型"),
SYSTEM_UNKNOWN_ERROR("-1", "系统繁忙,请稍后再试...");
private String code;
private String msg;
ErrorCodeEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
@Override
public String toString() {
return "ErrorCodeEnum{" +
"code='" + code + '\'' +
", msg='" + msg + '\'' +
'}';
}
public boolean isSuccessful() {
return this.code == ErrorCodeEnum.SUCCESS.getCode();
}
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() + " |");
});
}
}
package com.zzsn.knowbase.controller;
import com.zzsn.knowbase.service.ILocalFileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Version 1.0
* @Author: ZhangJingKun
* @Date: 2024/1/10 10:18
* @Content:
*/
@RestController
@RequestMapping("/api/file")
public class KbFileController {
@Autowired
private ILocalFileService localFileService;
/**
* 下载文档接口
* @param fileName
* @param filePath
* @param response
*/
@GetMapping("/download")
public void download(String fileName, String filePath, HttpServletResponse response) {
localFileService.download(fileName, filePath, response);
}
/**
* 编辑文档接口
* @param fileName
* @param filePath
* @param userName
* @param model
* @return
*/
@GetMapping("/edit")
public String editDocFile(String fileName, String filePath, String userName, Model model) {
return localFileService.editDocFile(fileName,filePath,userName,model);
}
/**
* 编辑文档时的回调接口
* @param request
* @param response
* @throws IOException
*/
@RequestMapping("/callback")
public void saveDocumentFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
//处理编辑回调逻辑
localFileService.callBack(request, response);
}
}
package com.zzsn.knowbase.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* onlyoffice定义的文档对象
* @author: zhangcx
* @date: 2019/8/7 16:30
*/
@ApiModel("文档实体")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Document implements Serializable {
/** 【必需】文件唯一标识 */
@ApiModelProperty(value = "文档 key", example="xYz123")
private String key;
/** 【必需】文档名称 */
@ApiModelProperty(value = "文档标题", example="test.doc")
private String title;
/** 【必需】文档后缀 */
@ApiModelProperty(value = "文档类型", example="doc")
private String fileType;
/** mimeType 应该先校验文件是否可以打开(非api必须字段) */
//private String mimeType;
/** 文件实体在服务器硬盘存储位置 */
@ApiModelProperty(value = "文档物理存储位置", example="/temp/test.doc")
private String storage;
/** 【必需】文件实体下载地址 */
@ApiModelProperty(value = "文档获取url", example="http://192.168.0.58:20053/api/file/xYz123")
private String url;
/** 打开文件预览/编辑的链接 */
//private String refrence;
/** 文档打开方式 {@link OpenModeEnum} */
//private String mode;
}
package com.zzsn.knowbase.entity;
import lombok.Builder;
import lombok.Data;
/**
* 文档编辑时参数 实体
* @author: zhangcx
* @date: 2019/8/26 14:50
*/
@Data
@Builder
public class DocumentEditParam {
/** 当前打开编辑页面的用户信息 */
private UserBean user;
/** onlyoffice在编辑时请求的回调地址,必选项 */
private String callbackUrl;
@Data
@Builder
public static class UserBean {
/** 用户id */
private String id;
/** 用户姓名 */
private String name;
}
}
package com.zzsn.knowbase.service;
import com.zzsn.knowbase.entity.Document;
import com.zzsn.knowbase.entity.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);
}
......@@ -4,8 +4,12 @@ import com.zzsn.knowbase.entity.KnowFile;
import com.zzsn.knowbase.vo.Result;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
......@@ -33,11 +37,32 @@ public interface ILocalFileService {
/**
* 文件下载
* @param
* @param
* @return
*/
//ResponseEntity<Resource> download(String fileName, String filePath);
void download(String fileName, String filePath, HttpServletResponse response);
/**
* 编辑文档
* @param fileName
* @param filePath
* @param userName
* @param model
* @return
*/
ResponseEntity<Resource> download(String fileName, String filePath);
String editDocFile(String fileName, String filePath, String userName, Model model);
/**
* 编辑文档时的回调接口
* @param request
* @param response
* @throws IOException
*/
void callBack(HttpServletRequest request, HttpServletResponse response) throws IOException;
}
package com.zzsn.knowbase.util;
import com.zzsn.knowbase.constant.ErrorCodeEnum;
/**
* 文档异常封装类
* @author zhangcx
*/
public final class DocumentException extends RuntimeException {
private ErrorCodeEnum errorCode;
public DocumentException(ErrorCodeEnum errorCode) {
super(errorCode.getMsg());
this.errorCode = errorCode;
}
public DocumentException(ErrorCodeEnum errorCode, Throwable t) {
super(errorCode.getMsg(), t);
this.errorCode = errorCode;
}
public ErrorCodeEnum getErrorCode() {
return errorCode;
}
}
......@@ -19,7 +19,7 @@
package com.zzsn.knowbase.util.file;
import com.zzsn.knowbase.constant.Constants;
import com.zzsn.knowbase.enums.DocumentType;
import com.zzsn.knowbase.constant.DocumentType;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
......@@ -123,6 +123,15 @@ public class DefaultFileUtility implements FileUtility {
String fileExt = fileName.substring(fileName.lastIndexOf("."));
return fileExt.toLowerCase();
}
//get file type from URL
public String getFileType(final String url) {
String fileName = getFileName(url);
if (fileName == null) {
return null;
}
String fileExt = fileName.substring(fileName.lastIndexOf("."));
return fileExt.toLowerCase();
}
// get an editor internal extension
public String getInternalExtension(final DocumentType type) {
......
......@@ -18,7 +18,7 @@
package com.zzsn.knowbase.util.file;
import com.zzsn.knowbase.enums.DocumentType;
import com.zzsn.knowbase.constant.DocumentType;
import java.nio.file.Path;
import java.util.List;
......@@ -29,6 +29,7 @@ public interface FileUtility {
String getFileName(String url); // get file name from its URL
String getFileNameWithoutExtension(String url); // get file name without extension
String getFileExtension(String url); // get file extension from URL
String getFileType(String url); // get file type from URL
String getInternalExtension(DocumentType type); // get an editor internal extension
List<String> getFileExts(); // get all the supported file extensions
List<String> getFillExts(); // get file extensions that can be filled
......
package com.zzsn.knowbase.util.file;
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);
}
}
......@@ -50,9 +50,18 @@ know:
checkuserurl: http://127.0.0.1:9988/sys/checkToken
getusersurl: http://127.0.0.1:9988/sys/user/thirdparty
document:
server:
host: http://gq55rd.natappfree.cc
files:
storage: E:/aaa/
storage: D:/know/
docservice:
url:
site: http://114.116.116.241:80/
converter: ConvertService.ashx
command: coauthoring/CommandService.ashx
api: web-apps/apps/api/documents/api.js
preloader: web-apps/apps/api/documents/cache-scripts.html
fillforms-docs: .docx|.oform
viewed-docs: .djvu|.oxps|.pdf|.xps
edited-docs: .csv|.docm|.docx|.docxf|.dotm|.dotx|.epub|.fb2|.html|.odp|.ods|.odt|.otp|.ots|.ott|.potm|.potx|.ppsm|.ppsx|.pptm|.pptx|.rtf|.txt|.xlsm|.xlsx|.xltm|.xltx
......
......@@ -22,10 +22,10 @@ class KnowBaseApplicationTests {
@Test
void contextLoads() throws IOException {
ResponseEntity<Resource> re = localFileService.download("abc.docx", "E:/aaa/abc.docx");
File file = re.getBody().getFile();
System.out.println(re);
// ResponseEntity<Resource> re = localFileService.download("abc.docx", "E:/aaa/abc.docx");
// File file = re.getBody().getFile();
//
// System.out.println(re);
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论