Commit 9984e414 by henry

添加onlyoffice服务

1 parent ae5dd7f0
Showing with 2118 additions and 4 deletions
package org.arch.storage.entity;
package org.arch.common.modules.base.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
......
......@@ -15,5 +15,18 @@
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.arch</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.arch</groupId>
<artifactId>storage</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package org.arch.office;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("org.arch.office")
@Slf4j
public class OnlyOfficeConfig {
public OnlyOfficeConfig(){
log.info("-----------------onlyoffice----------------");
}
}
package org.arch.office.config;/*
package com.eadc.office.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*") // SpringBoot2.4.0 [allowedOriginPatterns]代替[allowedOrigins]
.allowedMethods("*")
.maxAge(3600)
.allowCredentials(true);
}
}
*/
package org.arch.office.config;
import lombok.Data;
import org.arch.office.model.documentServer.EditorConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Data
@ConfigurationProperties(prefix = "office")
@Component
public class OnlyOfficeProperties {
// 本地服务ip地址
private String ip;
/**
* 编辑器默认语言【只允许中文!】
*/
private String lang;
/**
* logo信息
*/
private EditorConfig.Customization.Logo logo;
/**
* 文档服务器回调接口【交互接口非常非常重要】
*
*/
private String callbackUrl;
private String downloadUrl;
private String goBack;
/**
* 文档服务器js文件
*/
private String api;
// 可编辑、查看、转换的文件类型
private List<String> viewedDocs;
private List<String> editedDocs;
private List<String> convertDocs;
public Set<String> getFillExts(){
HashSet<String> result = new HashSet<>();
result.addAll(viewedDocs);
result.addAll(editedDocs);
result.addAll(convertDocs);
return result;
}
public String getCallbackUrl(){
return ip + callbackUrl;
}
public String getDownloadUrl(){
return ip + downloadUrl;
}
}
package org.arch.office.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.arch.office.model.entity.OnlyOfficeFile;
/**
* <p>
* Mapper 接口
* </p>
*
* @author SpicyRabbitLeg
* @since 2023-04-12
*/
public interface OnlyOfficeFileMapper extends BaseMapper<OnlyOfficeFile> {
}
package org.arch.office.model.documentServer;
public enum Action {
edit, // 编辑
review, // 编辑并开启审阅模式
view, // 查看
embedded, // 嵌入式
filter,
comment, // 查看并且开启审阅模式
chat,
fillForms,
blockcontent
}
package org.arch.office.model.documentServer;
import lombok.Data;
import java.util.ArrayList;
import java.util.HashMap;
/**
* 针对DocEditor需要的配置项
*/
@Data
public class Config {
// 文档配置项目
private Document document;
// 编辑类型
private DocumentType documentType;
// 文档编辑器定制化配置项
private EditorConfig editorConfig;
//token
private String token;
//显示类型
private String type;
public static class ConfigBuildr{
// 文件名称
private String filename;
// 文件后缀
private String fileSuffix;
}
public static Config build(){
Config config = new Config();
EditorConfig editorConfig = new EditorConfig();
Document document = new Document();
EditorConfig.Customization customization = new EditorConfig.Customization();
EditorConfig.Customization.Goback goBack = new EditorConfig.Customization.Goback();
EditorConfig.Customization.Logo logo = new EditorConfig.Customization.Logo();
editorConfig.setTemplates(new ArrayList<>(1));
editorConfig.setCoEditing(new HashMap<>());
customization.setLogo(logo);
customization.setGoback(goBack);
editorConfig.setCustomization(customization);
config.setEditorConfig(editorConfig);
document.setInfo(new Document.Info());
config.setDocument(document);
return config;
}
}
package org.arch.office.model.documentServer;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class DefaultCustomizationWrapper{
private Action action;
private EditorConfig.User user;
}
package org.arch.office.model.documentServer;
import lombok.Builder;
import lombok.Data;
import org.arch.common.modules.base.entity.File;
@Data
@Builder
public class DefaultDocumentWrapper {
private Document.Permission permission;
private String fileName;
private Boolean favorite;
private Boolean isEnableDirectUrl;
private File onlyOfficeFile;
}
package org.arch.office.model.documentServer;
import lombok.Builder;
import lombok.Data;
import org.arch.common.modules.base.entity.File;
@Data
@Builder
public class DefaultFileWrapper {
private String fileId;
private String fileName;
private String type;
private EditorConfig.User user;
private String lang;
private Action action;
private String actionData;
private Boolean canEdit;
private Boolean isEnableDirectUrl;
private File onlyOfficeFile;
}
package org.arch.office.model.documentServer;
import lombok.Data;
import java.util.List;
@Data
public class Document {
// 文档的附加参数(文档所有者、存储文档的文件夹、上传日期,共享设置)
private Info info;
// 文档是否被编辑和下载的权限
private Permission permissions;
// 文件类型 docx ppt
private String fileType;
// 文档服务器唯一标识
private String key;
// 文件名称
private String title;
// 文档服务器下载文件的地址
private String url;
@Data
public static class Info {
// 文件创建人的名称
private String owner;
// 收藏图标的高亮显示状态
private Boolean favorite;
// 文件上传日期
private String uploaded;
}
@Data
public static class Permission {
// 文档注释功能
private Boolean comment = true;
// 文档注释权限控制功能,根据用户组
private CommentGroup commentGroup;
// 文档copy功能
private Boolean copy = true;
// 文档下载功能
private Boolean download = true;
// 文档编辑功能(在model为view时这个设置不起作用)
private Boolean edit = true;
// 文档打印功能
private Boolean print = true;
// 文档填写表单功能
private Boolean fillForms = true;
// 文档过滤器功能
private Boolean modifyFilter = true;
// 文档更改内容控件设置功能
private Boolean modifyContentControl = true;
// 文档审阅模式功能
private Boolean review = true;
// 文档聊天功能
private Boolean chat = true;
// 定义用户可以接受/拒绝其更改的组
private List<String> reviewGroups;
private List<String> userInfoGroups;
@Data
public static class CommentGroup{
private List<String> view;
private List<String> edit;
private List<String> remove;
}
}
}
package org.arch.office.model.documentServer;
public enum DocumentType {
word,
cell,
slide
}
package org.arch.office.model.documentServer;
import lombok.Data;
import java.util.HashMap;
import java.util.List;
@Data
public class EditorConfig {
/**
* 定义文档服务器接收到数据后,处理后进行回调接口【系统交换接口】
* 。。。。
*/
private String callbackUrl;
// 文档共同编辑功能
private HashMap<String, Object> coEditing = null;
// 文档新建默认模板,搭配模板使用
private String createUrl;
private List<Template> templates;
// 文档编辑器自定义界面
private Customization customization;
// 文档嵌入式功能
private Embedded embedded;
// 文档语言,默认中文即可无需跟改
private String lang = "zh";
// 文档打开模式
private Mode mode = Mode.view;
// 文档操作用户
private User user;
@Data
public static class Customization {
// 文档编辑器logo
private Logo logo;
// 文档编辑器打开文档位置
private Goback goback;
// 文档编辑器自动保存功能
private Boolean autosave = true;
// 文档编辑器Comments菜单按钮
private Boolean comments = true;
// 文档编辑器菜单显示在最上放
private Boolean compactHeader = false;
// 文档编辑器toolbar显示模式
private Boolean compactToolbar = false;
// 文档编辑器使用OOXML格式兼容的功能,开启会缺失注释功能
private Boolean compatibleFeatures = false;
// 文档编辑器中保存文档时即调用回调接口
private Boolean forcesave = false;
// 文档编辑器显示帮助功能
private Boolean help = false;
// 文档编辑器第一次进行隐藏右边菜单
private Boolean hideRightMenu = false;
// 文档编辑器隐藏尺子
private Boolean hideRulers = false;
// 文档编辑器隐藏Submit按钮
private Boolean submitForm = false;
private Boolean about = true;
private Boolean feedback = true;
@Data
public static class Logo {
// 用户图片
private String image;
// 用户图片
private String imageEmbedded;
// 点击时的跳转地址
private String url;
}
@Data
public static class Goback {
// 文档开发文件位置
private String url;
}
}
@Data
public static class Embedded {
// 文档的绝对URL,作为嵌入文档的源文件
private String embedUrl;
// 允许文档保存到用户个人计算机上的绝对URL
private String saveUrl;
// 允许其他用户共享此文档的绝对URL
private String shareUrl;
// 嵌入查看器工具栏的位置可以在顶部或底部
private String toolbarDocked;
}
public enum Mode {
edit,
view
}
@Data
public static class User {
private String id;
private String name;
private String group;
private Boolean favorite;
public void configure(int idParam, String nameParam, String groupParam) {
this.id = "uid-" + idParam;
this.name = nameParam;
this.group = groupParam;
}
}
@Data
public static class Template {
// 模板图标
private String image;
// 模板名称
private String title;
// 模板下载地址
private String url;
}
}
package org.arch.office.model.documentServer;
public enum Status {
EDITING(1), // 1 - 文档正在编辑
SAVE(2), // 2 - 文档保存
CORRUPTED(3), // 3 - document saving error has occurred
MUST_FORCE_SAVE(6); // 6 - 文档正在编辑但是保存了文档;
private int code;
Status(final int codeParam) {
this.code = codeParam;
}
public int getCode() { // get document status
return this.code;
}
}
package org.arch.office.model.dto;
import lombok.Data;
import org.arch.office.model.documentServer.EditorConfig;
import java.util.List;
/**
* 文档服务器callback数据
*/
@Data
public class Callback {
// 文件类型 docx
private String filetype;
// 最新文档下载地址
private String url;
// 唯一key值
private String key;
// ...
private String changesurl;
// 历史记录
private History history;
// token
private String token;
// 定义执行强制保存请求时启动器的类型
private Integer forcesavetype;
// 当前的操作状态
private Integer status;
//全部用户存储用户id
private List<String> users;
// 只有当前操作人的信息
private List<Action> actions;
//
private String userdata;
//
private String lastsave;
private Boolean notmodified;
@Data
public static class History {
private String serverVersion;
private String key;
private Integer version;
private String created;
private EditorConfig.User user;
// private List<ChangesHistory> changes;
}
@Data
public static class Action {
// 用户id
private String userid;
// 操作类型
private Action type;
}
}
package org.arch.office.model.entity;
import lombok.Data;
/**
* 附件(aid, aname, apath, rid)
*
* ClassName: Attach
* Function: TODO
* Date: 2020/2/12 0012 20:25
* author XieWenYing
* version V1.0
*/
@Data
public class Attach {
private String aid;//附件Id
private String aname;//附件名
private String apath;//附件文件路径
//private String rid;//对应需求id
private String fileId;
}
package org.arch.office.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OnlyOfficeFile implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 文件id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 文件唯一标识
*/
private String fileKey;
/**
* 文件名称
*/
private String title;
/**
* 文件名称
*/
private String name;
/**
* 文件后缀
*/
private String suffix;
/**
* 文件content-type
*/
private String contentType;
/**
* 文件大小
*/
private Long length;
/**
* 文件真实地址
*/
private String url;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
private String creater;
/**
* 跟新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
* 跟新人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updater;
/**
* 状态
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer status;
/**
* 版本
*/
@Version
private Integer version;
/**
* md5值
*/
private String md5;
/**
* 过期时间
*/
private Long expiry;
}
package org.arch.office.model.entity;
import lombok.Data;
import java.util.List;
/**
* 每条需求(id, title, content, outline)
* ClassName: ReqBase
* Date: 2020/2/12 0012 19:36
* author XieWenYing
* version V1.0
*/
@Data
public class Reqbase {
private String rid;
private String title;
private String content;
private Integer outline;
private String pid;
private List<String> headingNotes;
private List<Attach> attachList;
}
package org.arch.office.model.vo;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.arch.office.service.Flag;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
public class OnlyOfficeFileVo {
@NotNull(message = "id不能为空",groups = Runnable.class)
private Long id;
@NotEmpty(message = "fileKey不能为空",groups = Flag.class)
private String fileKey;
@NotEmpty(message = "title不能为空",groups = Flag.class)
private String title;
@NotEmpty(message = "url不能为空",groups = Flag.class)
private String url;
private Long length;
private String contentType;
private String md5;
public String getName() {
if (StringUtils.isNotBlank(title)) {
return title.substring(0, title.lastIndexOf("."));
}
return StringUtils.EMPTY;
}
public String getSuffix() {
if (StringUtils.isNotBlank(title)) {
// 获取文件后缀
String suffix = title.substring(title.lastIndexOf(".") + 1);
return suffix;
}
return StringUtils.EMPTY;
}
}
package org.arch.office.service;
import org.arch.office.service.impl.CallbackHandler;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 处理文档服务器返回的数据
*/
public interface Callback {
int handle(org.arch.office.model.dto.Callback body, Long fileId);
int getStatus();
@Autowired
default void selfRegistration(CallbackHandler callbackHandler) {
callbackHandler.register(getStatus(), this);
}
}
package org.arch.office.service;
import java.io.UnsupportedEncodingException;
public interface Configurer<O, W> {
void configure(O instance, W wrapper) throws UnsupportedEncodingException;
}
package org.arch.office.service;
import org.arch.office.model.documentServer.EditorConfig;
public interface CustomizationConfigurer <W> extends Configurer<EditorConfig.Customization, W> {
void configure(EditorConfig.Customization customization, W wrapper);
}
package org.arch.office.service;
import org.arch.office.model.documentServer.Document;
public interface DocumentConfigurer <W> extends Configurer<Document, W> {
void configure(Document document, W wrapper);
}
package org.arch.office.service;
import org.arch.office.model.documentServer.EditorConfig;
public interface EditorConfigConfigurer<W> extends Configurer<EditorConfig, W> {
void configure(EditorConfig editorConfig, W wrapper);
}
package org.arch.office.service;
import org.arch.office.model.documentServer.Config;
import java.io.UnsupportedEncodingException;
public interface FileConfigurer<W> extends Configurer<Config, W>{
void configure(Config model, W wrapper) throws UnsupportedEncodingException;
Config getFileModel(W wrapper) throws UnsupportedEncodingException;
}
package org.arch.office.service;
public interface Flag {
}
package org.arch.office.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.arch.office.model.documentServer.Config;
import org.arch.office.model.entity.OnlyOfficeFile;
import org.arch.office.model.vo.OnlyOfficeFileVo;
import java.util.List;
public interface OnlyOfficeFileService {
Integer removeByIds(List<Long> ids);
OnlyOfficeFile updateById(OnlyOfficeFileVo onlyOfficeFileVo);
IPage<OnlyOfficeFile> queryByPage(Long current, Integer rows, OnlyOfficeFileVo onlyOfficeFileVo);
List<OnlyOfficeFile> queryByIds(List<Long> ids);
OnlyOfficeFile queryById(Long id);
OnlyOfficeFile saveOnlyOfficeFile(OnlyOfficeFileVo onlyOfficeFileVo);
/**
* 根据文件的id构建文档编辑器需要的config对象
*/
Config buildConfigByFileId(Long fileId);
}
package org.arch.office.service.impl;
import org.arch.office.service.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class CallbackHandler {
private final Logger logger = LoggerFactory.getLogger(CallbackHandler.class);
private final Map<Integer, Callback> callbackHandlers = new HashMap<>();
public void register(int code, Callback callback) {
callbackHandlers.put(code, callback);
}
public int handle(org.arch.office.model.dto.Callback body, Long fileId) {
Callback callback = callbackHandlers.get(body.getStatus());
if (callback == null) {
logger.warn("Callback status " + body.getStatus() + " is not supported yet");
return 0;
}
return callback.handle(body, fileId);
}
}
package org.arch.office.service.impl;
import org.arch.office.config.OnlyOfficeProperties;
import org.arch.office.model.documentServer.DefaultCustomizationWrapper;
import org.arch.office.model.documentServer.EditorConfig;
import org.arch.office.service.CustomizationConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CustomizationConfigurerImpl implements CustomizationConfigurer<DefaultCustomizationWrapper> {
@Autowired
private OnlyOfficeProperties onlyOfficeProperties;
@Override
public void configure(EditorConfig.Customization customization, DefaultCustomizationWrapper wrapper) {
customization.setSubmitForm(false);
customization.setLogo(onlyOfficeProperties.getLogo());
EditorConfig.Customization.Goback goback = new EditorConfig.Customization.Goback();
goback.setUrl(onlyOfficeProperties.getGoBack());
customization.setGoback(goback);
}
}
package org.arch.office.service.impl;
import org.arch.office.config.OnlyOfficeProperties;
import org.arch.office.model.documentServer.DefaultDocumentWrapper;
import org.arch.office.model.documentServer.Document;
import org.arch.office.service.DocumentConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DocumentConfigurerImpl implements DocumentConfigurer<DefaultDocumentWrapper> {
@Autowired
private OnlyOfficeProperties onlyOfficeProperties;
@Override
public void configure(Document document, DefaultDocumentWrapper wrapper) {
String fileName = wrapper.getFileName();
Document.Permission permission = wrapper.getPermission();
// 设置文件名称
document.setTitle(fileName);
// 设置访问地址
document.setUrl(onlyOfficeProperties.getDownloadUrl() + wrapper.getOnlyOfficeFile().getId());
document.setFileType(wrapper.getOnlyOfficeFile().getSuffix());
Document.Info info = document.getInfo();
info.setFavorite(wrapper.getFavorite());
info.setOwner(wrapper.getOnlyOfficeFile().getCreateMan().toString());
info.setUploaded(wrapper.getOnlyOfficeFile().getCreateTime().toString());
document.setKey(wrapper.getOnlyOfficeFile().getFileKey());
document.setPermissions(permission);
}
}
package org.arch.office.service.impl;
import org.arch.office.model.documentServer.Status;
import org.arch.office.service.Callback;
import org.springframework.stereotype.Service;
@Service
public class EditCallback implements Callback {
@Override
public int handle(org.arch.office.model.dto.Callback body, Long fileId) {
return 0;
}
@Override
public int getStatus() {
return Status.EDITING.getCode();
}
}
package org.arch.office.service.impl;
import org.arch.office.config.OnlyOfficeProperties;
import org.arch.office.model.documentServer.Action;
import org.arch.office.model.documentServer.DefaultCustomizationWrapper;
import org.arch.office.model.documentServer.DefaultFileWrapper;
import org.arch.office.model.documentServer.EditorConfig;
import org.arch.office.service.EditorConfigConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
@Service
public class EditorConfigConfigurerImpl implements EditorConfigConfigurer<DefaultFileWrapper> {
@Autowired
private OnlyOfficeProperties onlyOfficeProperties;
@Autowired
private CustomizationConfigurerImpl customizationConfigurer;
@Override
public void configure(EditorConfig editorConfig, DefaultFileWrapper wrapper) {
// 不显示新建
editorConfig.setTemplates(null);
editorConfig.setCreateUrl(null);
// 设置回调接口
editorConfig.setCallbackUrl(onlyOfficeProperties.getCallbackUrl() + wrapper.getOnlyOfficeFile().getId());
editorConfig.setLang("zh"); // 语言,写死不允许修改
Boolean canEdit = wrapper.getCanEdit();
Action action = wrapper.getAction();
editorConfig.setCoEditing(action.equals(Action.view) ? new HashMap<String, Object>() {{
put("mode", "strict");
put("change", false);
}} : new HashMap<String, Object>() {{
put("mode", "fast");
put("change", true);
}});
// define the customization configurer
customizationConfigurer.configure(editorConfig.getCustomization(),
DefaultCustomizationWrapper.builder()
.action(action)
.build());
// 设置Model
editorConfig.setMode(canEdit && !action.equals(Action.view) ? EditorConfig.Mode.edit : EditorConfig.Mode.view);
editorConfig.setUser(wrapper.getUser());
// 设置embedded
editorConfig.setEmbedded(null);
}
}
package org.arch.office.service.impl;
import org.arch.office.config.OnlyOfficeProperties;
import org.arch.office.model.documentServer.*;
import org.arch.office.service.FileConfigurer;
import org.arch.office.utils.FileUtility;
import org.arch.office.utils.JWTUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
@Service
public class FileConfigurerImpl implements FileConfigurer<DefaultFileWrapper> {
@Autowired
private FileUtility fileUtility;
@Autowired
private OnlyOfficeProperties onlyOfficeProperties;
@Autowired
private JWTUtil jwtUtil;
@Autowired
private DocumentConfigurerImpl documentConfigurer;
@Autowired
private EditorConfigConfigurerImpl editorConfigConfigurer;
@Override
public void configure(Config config, DefaultFileWrapper wrapper) throws UnsupportedEncodingException {
String fileName = wrapper.getOnlyOfficeFile().getTitle();
Action action = wrapper.getAction();
// 获取页面显示类型
DocumentType documentType = fileUtility.getDocumentType(wrapper.getOnlyOfficeFile().getSuffix());
config.setDocumentType(documentType);
config.setType(wrapper.getType());
// TODO 根据用户设置权限
Document.Permission userPermissions = new Document.Permission();
// 查看是否支持编辑
String fileExt = "." + wrapper.getOnlyOfficeFile().getSuffix();
Boolean canEdit = onlyOfficeProperties.getEditedDocs().contains(fileExt);
if ((!canEdit && action.equals(Action.edit) || action.equals(Action.fillForms)) && onlyOfficeProperties
.getFillExts().contains(fileExt)) {
canEdit = true;
wrapper.setAction(Action.fillForms);
}
wrapper.setCanEdit(canEdit);
DefaultDocumentWrapper documentWrapper = DefaultDocumentWrapper.builder()
.fileName(fileName)
.permission(updatePermissions(userPermissions, action, canEdit))
.favorite(wrapper.getUser().getFavorite())
.onlyOfficeFile(wrapper.getOnlyOfficeFile())
.build();
documentConfigurer.configure(config.getDocument(),documentWrapper);
editorConfigConfigurer.configure(config.getEditorConfig(),wrapper);
HashMap<String, Object> params = new HashMap<>();
params.put("type", config.getType());
params.put("documentType", config.getDocumentType());
params.put("document", config.getDocument());
params.put("editorConfig", config.getEditorConfig());
config.setToken(jwtUtil.onlyOfficeCreateToken(params));
}
@Override
public Config getFileModel(DefaultFileWrapper wrapper) throws UnsupportedEncodingException {
Config config = Config.build();
configure(config, wrapper);
return config;
}
private Document.Permission updatePermissions(final Document.Permission userPermissions, final Action action, final Boolean canEdit) {
userPermissions.setComment(
!action.equals(Action.view)
&& !action.equals(Action.fillForms)
&& !action.equals(Action.embedded)
&& !action.equals(Action.blockcontent)
);
userPermissions.setFillForms(
!action.equals(Action.view)
&& !action.equals(Action.comment)
&& !action.equals(Action.embedded)
&& !action.equals(Action.blockcontent)
);
userPermissions.setReview(canEdit
&& (action.equals(Action.review) || action.equals(Action.edit)));
userPermissions.setEdit(canEdit
&& (action.equals(Action.view)
|| action.equals(Action.edit)
|| action.equals(Action.filter)
|| action.equals(Action.blockcontent)));
return userPermissions;
}
}
package org.arch.office.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.arch.office.config.OnlyOfficeProperties;
import org.arch.office.mapper.OnlyOfficeFileMapper;
import org.arch.office.model.documentServer.Config;
import org.arch.office.model.entity.OnlyOfficeFile;
import org.arch.office.model.vo.OnlyOfficeFileVo;
import org.arch.office.service.OnlyOfficeFileService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.List;
@Service
public class OnlyOfficeFileServiceImpl implements OnlyOfficeFileService {
private final OnlyOfficeFileMapper onlyOfficeFileMapper;
private final OnlyOfficeProperties onlyOfficeProperties;
public OnlyOfficeFileServiceImpl (OnlyOfficeFileMapper onlyOfficeFileMapper,OnlyOfficeProperties onlyOfficeProperties){
this.onlyOfficeFileMapper = onlyOfficeFileMapper;
this.onlyOfficeProperties = onlyOfficeProperties;
}
@Override
public Integer removeByIds(List<Long> ids) {
return onlyOfficeFileMapper.deleteById(ids.get(0));
}
@Override
public OnlyOfficeFile updateById(OnlyOfficeFileVo onlyOfficeFileVo) {
Assert.isTrue(onlyOfficeFileVo.getId() != null,"file id is null!");
OnlyOfficeFile onlyOfficeFile = new OnlyOfficeFile();
BeanUtils.copyProperties(onlyOfficeFileVo,onlyOfficeFile);
String title = onlyOfficeFileVo.getTitle();
if(StringUtils.isNotBlank(title)) {
onlyOfficeFile.setName(onlyOfficeFileVo.getName());
onlyOfficeFile.setSuffix(onlyOfficeFileVo.getSuffix());
}
return onlyOfficeFileMapper.updateById(onlyOfficeFile) > 0 ? onlyOfficeFile : null;
}
@Override
public IPage<OnlyOfficeFile> queryByPage(Long current, Integer rows, OnlyOfficeFileVo onlyOfficeFileVo) {
LambdaQueryWrapper<OnlyOfficeFile> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ObjectUtils.isNotEmpty(onlyOfficeFileVo.getId()),OnlyOfficeFile::getId,onlyOfficeFileVo.getId());
wrapper.eq(StringUtils.isNotBlank(onlyOfficeFileVo.getTitle()),OnlyOfficeFile::getTitle,onlyOfficeFileVo.getTitle());
wrapper.eq(StringUtils.isNotBlank(onlyOfficeFileVo.getFileKey()),OnlyOfficeFile::getFileKey,onlyOfficeFileVo.getFileKey());
return onlyOfficeFileMapper.selectPage(new Page<>(current, rows), wrapper);
}
@Override
public List<OnlyOfficeFile> queryByIds(List<Long> ids) {
LambdaQueryWrapper<OnlyOfficeFile> wrapper = new LambdaQueryWrapper<>();
return new ArrayList<>();
}
@Override
public OnlyOfficeFile queryById(Long id) {
return onlyOfficeFileMapper.selectById(id);
}
@Override
public OnlyOfficeFile saveOnlyOfficeFile(OnlyOfficeFileVo onlyOfficeFileVo) {
OnlyOfficeFile onlyOfficeFile = new OnlyOfficeFile();
BeanUtils.copyProperties(onlyOfficeFileVo,onlyOfficeFile);
onlyOfficeFile.setId(null);
if(StringUtils.isNotBlank(onlyOfficeFileVo.getTitle())) {
onlyOfficeFile.setName(onlyOfficeFileVo.getName());
onlyOfficeFile.setSuffix(onlyOfficeFileVo.getSuffix());
}
return onlyOfficeFileMapper.insert(onlyOfficeFile) > 0 ? onlyOfficeFile : null;
}
@Override
public Config buildConfigByFileId(Long fileId) {
Config config = new Config();
return config;
}
}
package org.arch.office.service.impl;
import cn.hutool.core.lang.Assert;
import org.arch.base.utils.FileUtils;
import org.arch.common.modules.base.entity.File;
import org.arch.office.model.documentServer.Status;
import org.arch.office.service.Callback;
import org.arch.office.utils.HttpUtil;
import org.arch.office.utils.MD5Util;
import org.arch.office.utils.MockMultipartFile;
import lombok.RequiredArgsConstructor;
import org.arch.storage.mapper.FileMapper;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.FileStorageService;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
@RequiredArgsConstructor
public class SaveCallback implements Callback {
private final MD5Util md5Util;
private final FileMapper sysFileMapper;
private final FileStorageService fileStorageService;
@Override
public int handle(org.arch.office.model.dto.Callback body, Long fileId) {
File file = sysFileMapper.selectById(fileId);
Assert.notNull(file, "上传文件不能为空");
byte[] bytes = HttpUtil.downloadFile(body.getUrl(), null);
MultipartFile multipartFile = new MockMultipartFile(file.getTitle(), file.getTitle(), file.getContentType(), bytes);
FileInfo upload = fileStorageService.of(multipartFile)
.setPath(FileUtils.filePath()).upload();
Assert.notNull(file, "上传失败");
//DateTime dateTime = DateUtil.offsetDay(new Date(), 7);
//String url = fileStorageService.generatePresignedUrl(upload, dateTime);
String url = upload.getUrl();
File temp = new File();
temp.setId(file.getId());
temp.setUrl(url);
temp.setStoreFileName(upload.getObjectId());
String md5 = md5Util.encrypt(bytes);
temp.setMd5(md5);
temp.setFileKey(md5Util.key(md5));
temp.setTitle(file.getTitle());
sysFileMapper.updateById(temp);
// 历史记录信息
return 0;
}
@Override
public int getStatus() {
return Status.SAVE.getCode();
}
}
package org.arch.office.utils;
import org.arch.office.model.documentServer.DocumentType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class FileUtility {
private List<String> extsDocument = Arrays.asList(
"doc", "docx", "docm",
"dot", "dotx", "dotm",
"odt", "fodt", "ott", "rtf", "txt",
"html", "htm", "mht", "xml",
"pdf", "djvu", "fb2", "epub", "xps", "oform");
private List<String> extsSpreadsheet = Arrays.asList(
"xls", "xlsx", "xlsm", "xlsb",
"xlt", "xltx", "xltm",
"ods", "fods", "ots", "csv");
private List<String> extsPresentation = Arrays.asList(
"pps", "ppsx", "ppsm",
"ppt", "pptx", "pptm",
"pot", "potx", "potm",
"odp", "fodp", "otp");
/**
* 根据后缀名称返回文档类型
*/
public DocumentType getDocumentType(String suffix) {
suffix = suffix.toLowerCase();
if (extsDocument.contains(suffix)) {
return DocumentType.word;
}
if (extsSpreadsheet.contains(suffix)) {
return DocumentType.cell;
}
if (extsPresentation.contains(suffix)) {
return DocumentType.slide;
}
return DocumentType.word;
}
}
package org.arch.office.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 动态代理实现不了,JDK自带的基于接口的方式不能写static类型的方法,使用静态代理试一下
* <p>
* 基于java.net方式的http工具类
* * <dependency>
* * <groupId>org.apache.httpcomponents</groupId>
* * <artifactId>httpclient</artifactId>
* * </dependency>
* * <dependency>
* * <groupId>org.apache.httpcomponents</groupId>
* * <artifactId>httpmime</artifactId>
* * </dependency>
*/
public class HttpUtil {
private final static CloseableHttpClient httpClient;
private final static PoolingHttpClientConnectionManager poolManager;
private final static Logger logger = LoggerFactory.getLogger(HttpUtil.class);
static {
// 生成自定义的httpClient
RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();
SSLConnectionSocketFactory sslConnectionSocketFactory = getSslConnectionSocketFactory();
RegistryBuilder<ConnectionSocketFactory> builder = registryBuilder.register("http", PlainConnectionSocketFactory.INSTANCE);
if (sslConnectionSocketFactory != null) {
builder.register("https", sslConnectionSocketFactory);
}
poolManager = new PoolingHttpClientConnectionManager(builder.build());
poolManager.setMaxTotal(50);
poolManager.setDefaultMaxPerRoute(50);
HttpClientBuilder httpClientBuilder = HttpClients.custom().setConnectionManager(poolManager);
// 生成默认配置
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000)
.setSocketTimeout(5000)
.build();
httpClientBuilder.setDefaultRequestConfig(requestConfig);
List<Header> defaultHeader = new ArrayList<>();
defaultHeader.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"));
httpClientBuilder.setDefaultHeaders(defaultHeader);
httpClient = httpClientBuilder.build();
}
private static SSLConnectionSocketFactory getSslConnectionSocketFactory() {
try {
return new SSLConnectionSocketFactory(SSLContexts.custom().loadTrustMaterial(null, (x509Certificates, s) -> true).build(), new NoopHostnameVerifier());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static JSONObject get(String url, ArrayList<Header> headers) {
HttpGet httpGet = new HttpGet();
return doExecute(httpGet, url, null, headers);
}
public static JSONObject post(String url, Map<String, String> params, ArrayList<Header> headers) {
HttpPost httpPost = new HttpPost();
if (!CollectionUtils.isEmpty(params)) {
List<BasicNameValuePair> formParam = new ArrayList<>();
params.forEach((k, v) -> formParam.add(new BasicNameValuePair(k, v)));
UrlEncodedFormEntity from = new UrlEncodedFormEntity(formParam, Consts.UTF_8);
from.setContentType(new BasicHeader("Content-Type", ContentType.create("application/x-www-form-urlencoded", StandardCharsets.UTF_8).getMimeType()));
httpPost.setEntity(from);
}
return doExecute(httpPost, url, params, headers);
}
public static JSONObject postJson(String url, Map<String, String> params, ArrayList<Header> headers) {
HttpPost httpPost = new HttpPost();
if (!CollectionUtils.isEmpty(params)) {
StringEntity entity = new StringEntity(JSON.toJSONString(params), StandardCharsets.UTF_8);
entity.setContentType(new BasicHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType()));
entity.setContentEncoding(StandardCharsets.UTF_8.name());
httpPost.setEntity(entity);
}
return doExecute(httpPost, url, params, headers);
}
public static JSONObject postJsonString(String url, String json, ArrayList<Header> headers) {
HttpPost httpPost = new HttpPost();
if (!StringUtils.isEmpty(json)) {
StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8);
entity.setContentType(new BasicHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType()));
entity.setContentEncoding(StandardCharsets.UTF_8.name());
httpPost.setEntity(entity);
}
return doExecute(httpPost, url, JSON.parseObject(json, Map.class), headers);
}
public static byte[] downloadFile(String url, Map<String, String> headers) {
CloseableHttpResponse response = null;
try {
url = URLDecoder.decode(url, StandardCharsets.UTF_8.name());
HttpGet httpPost = new HttpGet(url);
// 设置header
if (!CollectionUtils.isEmpty(headers)) {
headers.forEach(httpPost::setHeader);
}
response = httpClient.execute(httpPost);
if (HttpStatus.OK.value() == response.getStatusLine().getStatusCode()) {
logger.info("==========请求成功,请求地址: {},请求状态信息: {}", url, response.getStatusLine());
return EntityUtils.toByteArray(response.getEntity());
}
logger.error("==========请求失败,请求地址: {},请求状态信息: {}", url, response.getStatusLine());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (response != null) {
try {
response.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return new byte[]{};
}
private static JSONObject doExecute(HttpRequestBase httpRequest, String url, Map<String, String> params, ArrayList<Header> headers) {
CloseableHttpResponse response = null;
JSONObject result = new JSONObject();
try {
httpRequest.setURI(new URI(URLDecoder.decode(url, StandardCharsets.UTF_8.name())));
if (!CollectionUtils.isEmpty(headers)) {
headers.forEach(httpRequest::setHeader);
}
response = httpClient.execute(httpRequest);
if (HttpStatus.OK.value() == response.getStatusLine().getStatusCode()) {
logger.info("==========请求成功,请求地址: {},请求状态信息: {},请求入参: {}", url, response.getStatusLine(), params);
result = JSON.parseObject(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
} else {
logger.error("==========请求失败,请求地址: {},请求状态信息: {},请求入参: {}", url, response.getStatusLine(), params);
}
} catch (Exception e) {
logger.error("==========请求失败,请求地址: {},请求状态信息: {},请求入参: {}", url, response != null ? response.getStatusLine() : "", params);
e.printStackTrace();
} finally {
if (response != null) {
try {
response.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return result;
}
}
package org.arch.office.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
@Component
public class JWTUtil {
@Value("${token.secret}")
private String secret;
@Value("${token.expire}")
private Integer expire;
/**
* only office获取token
*/
public String onlyOfficeCreateToken(Map<String,Object> params) {
try {
JWTCreator.Builder builder = JWT.create();
builder.withExpiresAt(getExpire());
Class<? extends JWTCreator.Builder> aClass = builder.getClass();
Method addClaim = aClass.getDeclaredMethod("addClaim", String.class,Object.class);
addClaim.setAccessible(true);
for (Map.Entry<String, Object> entry : params.entrySet()) {
addClaim.invoke(builder,entry.getKey(),entry.getValue());
}
return builder.sign(Algorithm.HMAC256(secret));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private Date getExpire() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, expire);
return calendar.getTime();
}
}
package org.arch.office.utils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Random;
@Component
public class MD5Util {
/**
* 获取md5字符串
*/
public String encrypt(String dataStr) {
return encrypt(dataStr.getBytes(StandardCharsets.UTF_8));
}
public String encrypt(byte[] bytes){
try {
MessageDigest m = MessageDigest.getInstance("MD5");
m.update(bytes);
byte s[] = m.digest();
String result = "";
for (int i = 0; i < s.length; i++) {
result += Integer.toHexString((0x000000FF & s[i]) | 0xFFFFFF00).substring(6);
}
return result;
} catch (Exception e) {
e.printStackTrace();
}
return StringUtils.EMPTY;
}
public String key(String u){
Random random = new Random();
StringBuilder sbr = new StringBuilder();
for (int i = 0; i < 10; i++) {
sbr.append(u.charAt(random.nextInt(u.length())));
}
return sbr.toString();
}
}
package org.arch.office.utils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class MockMultipartFile implements MultipartFile {
private final String name;
private String originalFilename;
@Nullable
private String contentType;
private final byte[] content;
public MockMultipartFile(String name, @Nullable byte[] content) {
this(name, "", (String)null, (byte[])content);
}
public MockMultipartFile(String name, InputStream contentStream) throws IOException {
this(name, "", (String)null, (byte[])FileCopyUtils.copyToByteArray(contentStream));
}
public MockMultipartFile(String name, @Nullable String originalFilename, @Nullable String contentType, @Nullable byte[] content) {
Assert.hasLength(name, "Name must not be null");
this.name = name;
this.originalFilename = originalFilename != null ? originalFilename : "";
this.contentType = contentType;
this.content = content != null ? content : new byte[0];
}
public MockMultipartFile(String name, @Nullable String originalFilename, @Nullable String contentType, InputStream contentStream) throws IOException {
this(name, originalFilename, contentType, FileCopyUtils.copyToByteArray(contentStream));
}
public String getName() {
return this.name;
}
public String getOriginalFilename() {
return this.originalFilename;
}
@Nullable
public String getContentType() {
return this.contentType;
}
public boolean isEmpty() {
return this.content.length == 0;
}
public long getSize() {
return (long)this.content.length;
}
public byte[] getBytes() throws IOException {
return this.content;
}
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(this.content);
}
public void transferTo(File dest) throws IOException, IllegalStateException {
FileCopyUtils.copy(this.content, dest);
}
}
package org.arch.office.utils;
import com.benjaminwan.ocrlibrary.OcrResult;
import io.github.mymonstercat.Model;
import io.github.mymonstercat.ocr.InferenceEngine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
/**
* 识别图片文字工具类
*/
@Slf4j
public class OCRUtils {
/**
* 通过你本地图片获取图片内容
*
* @param path 本地图片地址
* @return
* @throws Exception
*/
public static String getLocalImageContent(String path) throws Exception {
InferenceEngine engine = InferenceEngine.getInstance(Model.ONNX_PPOCR_V4);
File imgFile = new File(path);
OcrResult ocrResult = engine.runOcr(imgFile.getPath());
return ocrResult.getStrRes().trim();
}
/**
* 通过网络图片获取图片内容
*
* @param httpUrl 网络图片地址
* @return
* @throws Exception
*/
public static String getNetImageContent(String httpUrl) throws Exception {
URL imageUrl = new URL(httpUrl);
File imgFile = new File("image.png");
FileUtils.copyURLToFile(imageUrl, imgFile);
InferenceEngine engine = InferenceEngine.getInstance(Model.ONNX_PPOCR_V4);
OcrResult ocrResult = engine.runOcr(imgFile.getPath());
return ocrResult.getStrRes().trim();
}
/**
* 通过图片流获取图片中的内容
*
* @param imageStream 图片流
* @return
* @throws Exception
*/
public static String getIoImageContent(InputStream imageStream) throws Exception {
BufferedImage bufferedImage = ImageIO.read(imageStream);
// 创建临时图片文件
File imgFile = File.createTempFile("tempImage", ".png");
ImageIO.write(bufferedImage, "png", imgFile);
InferenceEngine engine = InferenceEngine.getInstance(Model.ONNX_PPOCR_V4);
OcrResult ocrResult = engine.runOcr(imgFile.getPath());
if (imgFile.exists()) {
imgFile.delete();
log.info("删除临时文件");
}
return ocrResult.getStrRes().trim();
}
public static void main(String[] args) throws Exception {
//1:通过本地文件获取图片内容
String localImage = "C:\\Users\\hepen\\Desktop\\word校验\\c58b400e2b574243a1b3248d0a5d43ea\\5b7a58e73c444e22beee48a19421e977-27.png";
System.out.println(getLocalImageContent(localImage));
//2:通过网络文件获取图片内容
String imageUrl = "http://www.yangguangqin.com/uploads/image/20200616/1592298653190882.png";
System.out.println(getNetImageContent(imageUrl));
//3:通过网络流获取图片内容
File file = new File(localImage);
InputStream inputStream = new FileInputStream(file);
String ioImageContent = getIoImageContent(inputStream);
System.out.println(ioImageContent);
}
}
package org.arch.office.utils;
import com.eadc.entity.vo.UpLoadVO;
import com.eadc.service.OssService;
import com.eadc.utils.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* 解析word工具类
*/
@Service
@Slf4j
public class ParseWordUtils {
public static ParseWordUtils parseUtils;
@Resource
private OssService ossService;
@PostConstruct
public void init() {
parseUtils = this;
parseUtils.ossService = this.ossService;
}
/**
* 通过指定关键字截取word中输入的两个关键字之间的段落,并将改中间的段落复制到另外一个新的word中
* @param fis 源文件流
* @param keywordStart 开始关键字
* @param keywordEnd 结束关键字
* @param ignorePage 忽略的页面
*/
public static UpLoadVO copyContentByKeywordRange(InputStream fis,
String keywordStart,
String keywordEnd,
Integer ignorePage) throws IOException {
ZipSecureFile.setMinInflateRatio(-1.0d);
File temp = File.createTempFile("temp-", ".docx");
try (
FileOutputStream fos = new FileOutputStream(temp)) {
XWPFDocument sourceDocument = new XWPFDocument(fis);
XWPFDocument targetDocument = new XWPFDocument();
boolean isCopying = false;
if(null==ignorePage){
ignorePage = 0;
}
int pageNum = 0;
for (IBodyElement element : sourceDocument.getBodyElements()) {
if (element instanceof XWPFParagraph) {
// 计算页数
pageNum += 1;
if (pageNum < ignorePage) {
continue;
}
XWPFParagraph paragraph = (XWPFParagraph) element;
String text = paragraph.getText();
if (text.contains(keywordStart)) {
isCopying = true;
}
if (isCopying) {
if (paragraph.getCTP().getPPr() != null && paragraph.getCTP().getPPr().getTabs() != null) {
XWPFParagraph newParagraph = targetDocument.createParagraph();
newParagraph.getCTP().setPPr(paragraph.getCTP().getPPr());
extracted(paragraph, newParagraph);
} else {
XWPFParagraph newParagraph = targetDocument.createParagraph();
extracted(paragraph, newParagraph);
}
}
if (text.contains(keywordEnd)) {
isCopying = false;
}
} else if (element instanceof XWPFTable && isCopying) {
XWPFTable table = (XWPFTable) element;
XWPFTable newTable = targetDocument.createTable();
newTable.getCTTbl().set(table.getCTTbl());
} else if (element instanceof XWPFChart) {
if (isCopying) {
XWPFChart chart = (XWPFChart) element;
XWPFChart newChart = targetDocument.createChart();
newChart.getCTChart().set(chart.getCTChart().copy());
}
}
}
targetDocument.write(fos);
FileItem fileItem = FileUtils.createFileItem(temp);
MultipartFile multipartFile = new CommonsMultipartFile(fileItem);
UpLoadVO upload = parseUtils.ossService.upload(multipartFile);
log.info("获取上传文件的id为:{}",upload.getFileId());
//删除临时文件
temp.delete();
return upload;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static void extracted(XWPFParagraph paragraph, XWPFParagraph newParagraph) throws Exception {
for (XWPFRun run : paragraph.getRuns()) {
XWPFRun newRun = newParagraph.createRun();
newRun.setText(run.getText(0));
newRun.setBold(run.isBold());
newRun.setItalic(run.isItalic());
for (XWPFPicture picture : run.getEmbeddedPictures()) {
byte[] pictureData = picture.getPictureData().getData();
int pictureType = picture.getPictureData().getPictureType();
newRun.addPicture(new ByteArrayInputStream(pictureData), pictureType, "Copied Image",
Units.toEMU(400), Units.toEMU(400));
}
}
}
/**
* 通过关键字获取包含关键字的行
* @param pdfPath
* @param searchKeyWord
* @return
* @throws IOException
*/
public static List<String> parsePdf(String pdfPath,String searchKeyWord) throws IOException {
List<String> arrList = new ArrayList<>();
PDDocument document = null;
try {
File file = new File(pdfPath);
document = PDDocument.load(file);
PDFTextStripper pdfStripper = new PDFTextStripper();
String text = pdfStripper.getText(document);
String[] lines = text.split(System.lineSeparator()); // 将文本内容按行分割成数组
String keyword = searchKeyWord; // 指定关键字
for (String line : lines) {
if (line.contains(keyword)) { // 检查每行是否包含指定关键字
log.info("查询指定的关键字的行为:{}",line);
arrList.add(line);
}
}
return arrList;
} catch (IOException e) {
e.printStackTrace();
} finally {
if(null!=document){
document.close();
}
}
return null;
}
public static void main(String[] args) throws Exception {
String filePath="C:\\Users\\hepen\\Desktop\\word校验\\概要设计.docx";
File file = new File("F:\\test-word\\test1.docx");
InputStream inputStream1 = new FileInputStream(file);
InputStream inputStream2 = new FileInputStream(new File(filePath));
//copyContentByKeywordRange(inputStream, "1.监督评价考核", "2.日常运营",10);
}
}
package org.arch.office.utils;
import cn.hutool.core.lang.Assert;
import com.eadc.modules.system.service.dto.File;
import com.eadc.modules.system.service.mapper.FileMapper;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.dromara.x.file.storage.core.Downloader;
import org.dromara.x.file.storage.core.FileStorageService;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* 解析pdf工具类【对于扫描版的pdf是无法解析的】
*/
@Service
public class PrasePdfUtils {
public static PrasePdfUtils prasePdfUtils;
@Resource
private FileStorageService fileStorageService;
@Resource
private FileMapper sysFileMapper;
@PostConstruct
public void init() {
prasePdfUtils = this;
prasePdfUtils.fileStorageService = this.fileStorageService;
prasePdfUtils.sysFileMapper = this.sysFileMapper;
}
public static String prasePdf(String fileId) throws IOException {
PDDocument document = null;
ByteArrayInputStream byteArrayInputStream = null;
String text = "";
try{
File file = prasePdfUtils.sysFileMapper.selectById(fileId);
Assert.notNull(file, "文件不存在");
Downloader download = prasePdfUtils.fileStorageService.download(file.getUrl());
byte[] bytes = download.bytes();
byteArrayInputStream = new ByteArrayInputStream(bytes);
document = PDDocument.load(byteArrayInputStream);
// 创建PDFTextStripper对象并从文档中提取文本
PDFTextStripper pdfStripper = new PDFTextStripper();
text = pdfStripper.getText(document);
return text;
}catch (Exception e){
e.printStackTrace();
} finally {
if(null!=document){
document.close();
}
if(null!=byteArrayInputStream){
byteArrayInputStream.close();
}
}
return null;
}
}
package org.arch.office.utils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.util.UUID;
/**
* word转图片工具类
*/
public class WordConvertImageUtils {
public static void wordChangeImage(InputStream fileStream, String targetPath){
ZipSecureFile.setMinInflateRatio(-1.0d);
try {
PDDocument document = PDDocument.load(fileStream);
String dirName = UUID.randomUUID().toString().replaceAll("-","").toUpperCase();
String filePath = targetPath +File.separator + dirName;
File file = new File(filePath);
if(!file.exists()){
file.mkdir();
}
PDFRenderer pdfRenderer = new PDFRenderer(document);
for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 70); // 设置DPI,可以调整输出图片的分辨率
ImageIO.write(bim, "PNG", new File(filePath+ File.separator + (UUID.randomUUID()
.toString().replaceAll("-","")+"-"+(page + 1)) + ".png"));
}
document.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/* public static void main(String[] args) throws Exception {
String targetPath="C:\\测试pdf转图片.pdf";
File file = new File(targetPath);
FileInputStream fileInputStream = new FileInputStream(file);
wordChangeImage(fileInputStream,"F:\\");
}*/
}
package org.arch.office.utils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
public class WordUtils {
public static MultipartFile convertToMultipartFile(String filename, String contentType, InputStream inputStream) throws IOException {
return new MockMultipartFile(filename, filename, contentType, inputStream);
}
}
package org.arch.office.utils;
import com.microsoft.schemas.office.office.CTOLEObject;
import com.microsoft.schemas.vml.CTShape;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* ClassName: XWPFUtils
* Function: TODO
* Date: 2020/2/12 0012 21:09
* author XieWenYing
* version V1.0
*/
public class XWPFUtils {
/**
* 获取某一段落中图片和对象的索引
* @param paragraph
* @return
*/
public static Map<String, List<String>> readAttachInParagraph(XWPFParagraph paragraph) {
//图片索引和Object索引获取map
HashMap map = new HashMap<>();
//图片索引List
List<String> imageBundleList = new ArrayList<>();
//Object索引List
ArrayList<String> objectBundleList = new ArrayList<>();
List<XWPFRun> runList = paragraph.getRuns();
for (XWPFRun run : runList) {
CTR ctr = run.getCTR();
//对子元素进行遍历
XmlCursor xmlCursor = ctr.newCursor();
//拿到所有子元素
xmlCursor.selectPath("./*");
while (xmlCursor.toNextSelection()) {
XmlObject o = xmlCursor.getObject();
//如果子元素是<w:drawing>这样的形式, 使用CTDrawing保存图片
if (o instanceof CTDrawing) {
CTDrawing drawing = (CTDrawing) o;
List<CTInline> ctInlines = drawing.getInlineList();
for (CTInline inline : ctInlines) {
CTGraphicalObject graphic = inline.getGraphic();
XmlCursor cursor = graphic.getGraphicData().newCursor();
cursor.selectPath("./*");
while (cursor.toNextSelection()) {
XmlObject object = cursor.getObject();
//如果子元素是<pic:pic>这种形式
if (object instanceof CTPicture) {
CTPicture picture = (CTPicture) object;
//拿到元素的属性
imageBundleList.add(picture.getBlipFill().getBlip().getEmbed());
}
}
}
}
//使用CTObject保存图片
//<w:object>形式
if (o instanceof CTObject) {
CTObject object = (CTObject) o;
XmlCursor cursor = object.newCursor();
cursor.selectPath("./*");
CTShape shape;
CTOLEObject oleObject;
while (cursor.toNextSelection()) {
XmlObject xmlObject = cursor.getObject();
//如果是图片类型,存图片id
if (xmlObject instanceof CTShape) {
shape = (CTShape) xmlObject;
imageBundleList.add(shape.getImagedataArray(0).getId2());
}
//如果是嵌入对象类型,存对象id
if (xmlObject instanceof CTOLEObject) {
oleObject = (CTOLEObject) xmlObject;
objectBundleList.add(oleObject.getId());
}
}
}
}
}
map.put("img", imageBundleList);
map.put("object", objectBundleList);
return map;
}
/**
* 获取某一段落的大纲级别
* @param document
* @param paragraph
* @return
*/
public static BigInteger getParaOutlineLvl(XWPFDocument document, XWPFParagraph paragraph) {
XWPFStyles styles = document.getStyles();
XWPFStyle style = styles.getStyle(paragraph.getStyle());
//判断该段落是否设置了大纲级别
if (paragraph.getCTP().getPPr().getOutlineLvl() != null) {
//System.out.println(paragraph.getParagraphText());
//System.out.println(paragraph.getCTP().getPPr().getOutlineLvl().getVal());
return paragraph.getCTP().getPPr().getOutlineLvl().getVal();
//判断该段落的样式是否设置了大纲级别
} else if (style != null && style.getCTStyle().getPPr().getOutlineLvl() != null) {
//System.out.println(paragraph.getParagraphText());
//System.out.println(style.getCTStyle().getPPr().getOutlineLvl().getVal());
return style.getCTStyle().getPPr().getOutlineLvl().getVal();
//判断该段落的基础样式是否设置了大纲级别
} else if (style != null && style.getCTStyle()!=null && style.getCTStyle().getBasedOn()!= null&&
styles.getStyle(style.getCTStyle().getBasedOn().getVal()).getCTStyle().getPPr().getOutlineLvl() != null) {
//System.out.println(paragraph.getParagraphText());
String styledName = style.getCTStyle().getBasedOn().getVal();
//System.out.println(styles.getStyle(styledName).getCTStyle().getPPr().getOutlineLvl().getVal());
return styles.getStyle(styledName).getCTStyle().getPPr().getOutlineLvl().getVal();
//没有设置大纲级别
} else {
//System.out.println(paragraph.getParagraphText()+"==");
return null;
}
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.arch.office.OnlyOfficeConfig
......@@ -4,7 +4,7 @@ import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import lombok.SneakyThrows;
import org.arch.base.utils.MD5Utils;
import org.arch.storage.entity.File;
import org.arch.common.modules.base.entity.File;
import org.dromara.x.file.storage.core.FileInfo;
import org.springframework.web.multipart.MultipartFile;
......
......@@ -2,7 +2,7 @@ package org.arch.storage.service.impl;
import cn.hutool.core.io.file.FileNameUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.arch.storage.entity.File;
import org.arch.common.modules.base.entity.File;
import org.arch.storage.mapper.FileMapper;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.recorder.FileRecorder;
......
......@@ -6,7 +6,7 @@ import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.arch.base.utils.FileUtils;
import org.arch.storage.entity.File;
import org.arch.common.modules.base.entity.File;
import org.arch.common.modules.base.dto.DelDTO;
import org.arch.common.modules.base.dto.DownloadObjectDTO;
import org.arch.common.modules.base.vo.UpLoadVO;
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!