kkFileView代码分析(四)——office文件的转换(1)office插件管理

2021SC@SDUSC

目录

前言:

office插件管理分析:

1、public void startOfficeManager(); 启动Office组件进程

2、public OfficeDocumentConverter getDocumentConverter()

3、private Map getLoadProperties()

4、private boolean killProcess():判断当前系统中是否已经存在正在运行的office组件进程。

5、public void destroyOfficeManager();


前言:

       该项目支持预览多种office文件如:doc、docx文档,word文档,ppt、pptx文档预览,word预览,pdf文档。其中word文档,pptx、ppt文档等有两种预览方式:图片预览、pdf预览。

  • 图片预览:文件大,前台加载过慢
  • pdf预览:内网访问,加载pdf快

        对office文件的操作大多都依赖org.artofsolving.jodconverter包,相比com.artofsolving. jodconverter而言,org能操作的文件类型更多,但是文字清晰度比com低一点。 

        如果平均上传的文件不大于5M,且不超过5个文件,org包可以在10秒内处理完成。 但有个性能问题,文件超过20M时会出现超时,而源码中设置的单个pdf转换任务的执行时间是120s,如果文件太大就会导致超时报错,并重新进行连接,处理下一个任务。包的maven配置如下:

        <dependency>
            <groupId>org.artofsolving.jodconverter</groupId>
            <artifactId>jodconverter-core</artifactId>
            <version>3.0-beta-4</version>
        </dependency>

office插件管理分析:

         该项目中涉及到很多office文件的转换,office发展时间很长,已经是一个庞大、稳定的文件体系,里面有非常多复杂的规则,单靠我们自己是很难写出一个office文件转换的功能插件来的,但是好在现在市场上提供了一些可以操作office的接口,只要使用好接口即可在我们个人的项目中实现office文件转换。

项目中使用的office工具类如下:

        import org.artofsolving.jodconverter.OfficeDocumentConverter;

        import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration;

        import org.artofsolving.jodconverter.office.OfficeManager;

        import org.artofsolving.jodconverter.office.OfficeUtils;

maven配置如下:

        OfficePluginManager类是实现office文件管理的基础,提供对office文件的相关操作。在早期的系统版本中,这个类的名字叫ConverterUtils,实现功能大致一样,本文就接受较新的版本。OfficePluginManager 类中定义变量如下:


    private final Logger logger = LoggerFactory.getLogger(OfficePluginManager.class);

    private OfficeManager officeManager;

    @Value("${office.plugin.server.ports:2001,2002}")
    private String serverPorts;

    @Value("${office.plugin.task.timeout:5m}")
    private String timeOut;

1、public void startOfficeManager(); 启动Office组件进程

        准备工作:先定义一个File变量来检测是否存在office组件可以对office文件进行操作;开始对文件转换之前要结束office进程。

        在方法定义处看到了@PostConstruct注解,该注解被用来修饰一个非静态的void方法,是一种JSR-250的规范。被@PostConstruct修饰的方法当bean创建完成的时候,会后置执行@PostConstruct修饰的方法,并且只会被服务器执行一次。通常我们会是在Spring框架中使用到@PostConstruct注解,该注解的方法在整个Bean初始化中的执行顺序是:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)。

        该方法中首先声明File变量officeHome,通过调用officeUtils工具类的getDefaultOfficeHome方法获取,如果officehome为空,说明在系统中找不到office组件建立所需的条件,office.home的配置有误会,抛出运行异常提醒用户确认office.home的配置。若可以定义成功说明office组件配置没有问题,进入下一行代码声明bool变量killOffice,调用killProcess方法赋值,这个方法我们稍后进行具体分析。如果killProcess执行有返回true,说明此时系统中已经存在一个正在运行的office组件进程,则将会自动结束现在这条进程。

    public void startOfficeManager(){
        File officeHome = OfficeUtils.getDefaultOfficeHome();
        if (officeHome == null) {
            throw new RuntimeException("找不到office组件,请确认'office.home'配置是否有误");
        }
        boolean killOffice = killProcess();
        if (killOffice) {
            logger.warn("检测到有正在运行的office进程,已自动结束该进程");
        }
        try {
            DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
            configuration.setOfficeHome(officeHome);
            String []portsString = serverPorts.split(",");

            int[] ports = Arrays.stream(portsString).mapToInt(Integer::parseInt).toArray();

            configuration.setPortNumbers(ports);
            long timeout = DurationStyle.detectAndParse(timeOut).toMillis();
            // 设置任务执行超时为5分钟
            configuration.setTaskExecutionTimeout(timeout);
            // 设置任务队列超时为24小时
            //configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);
            officeManager = configuration.buildOfficeManager();
            officeManager.start();
        } catch (Exception e) {
            logger.error("启动office组件失败,请检查office组件是否可用");
            throw e;
        }
    }

        在下一行try{}catch{}中声明DefaultOfficeManagerConfiguration configuration变量,为之前声明的officehome设置安装目录,设计端口数为队列中的流数,设置任务执行超时为五分钟,设置任务队列超时为24小时,最后将configuration赋值给officemanagereManager:officeManager = configuration.buildOfficeManager(),最后启动officeManager线程: officeManager.start(),为项目中的office文件提高转换接口和资源。

2、public OfficeDocumentConverter getDocumentConverter()

        实例化OfficeDocumentConverter变量converter ,为converter设置好默认加载参数后返回文件转换器converter。

    public OfficeDocumentConverter getDocumentConverter() {
        OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager, new OfficePluginExtendFormatRegistry());
        converter.setDefaultLoadProperties(getLoadProperties());
        return converter;
    }

3、private Map<String,?> getLoadProperties()

        实例化HaspMap对象loadproperties,设置"Hidden"为 true:设置"ReadOnly",为true转换文件时隐藏进程;设置 "UpdateDocMode",为UpdateDocMode.QUIET_UPDATE;设置"CharacterSet"为 StandardCharsets.UTF_8.name()即字符集为UTF-8。

    private Map<String,?> getLoadProperties() {
        Map<String,Object> loadProperties = new HashMap<>(10);
        loadProperties.put("Hidden", true);
        loadProperties.put("ReadOnly", true);
        loadProperties.put("UpdateDocMode", UpdateDocMode.QUIET_UPDATE);
        loadProperties.put("CharacterSet", StandardCharsets.UTF_8.name());
        return loadProperties;
    }

         Map是一个接口,即Interface Map<K,V>,它的每个元素包含一个key和一个value对象,在这两个对象之间存在一种映射的对应关系。所有从Map集合中访问元素时,只有指定了key就可以找到对应的value,因此key必须是唯一的且不能重复,当key相同时,后面的value值会覆盖之前的value值。

        HashMap,即Class HashMap<K,V>,是基于哈希表的Map接口实现,提供所有可选的映射操作,并允许空值和空键。这个类不保证map的顺序,特别是不保证该顺序会随着时间的推移保持不变!!

        这个方法用来获取加载参数,设置如下:系统对上传的文件只能读、进程在工作时隐藏,字符集是UTF-8类型。

4、private boolean killProcess():判断当前系统中是否已经存在正在运行的office组件进程。

        该方法在启动office组件时调用,一个系统中只需要存在一个office组件,因此需要在建立组件时判断一下当前是否存在,若存在则销毁。

        首先声明Propreties变量props,调用System.getProperties获取系统参数,主要是为了获取操作系统的名称。之后在try{}catch{}语句中调用props.getProperty("os.name"))获取当前项目运行的操作系统。

        若当前项目是在windows系统下,声明Process变量p,调用Runtime.getRuntime类获取Runtime类的实例,runtime是单实例的,每个Java应用程序都有一个该类的实例,它允许应用程序和运行应用程序的环境进行交互。使用exec方法执行字符串命令并返回一个process对象。

        声明ByteArrayOutputStream对象baos在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中。创建字节数组输出流对象有以下几种方式。之后再声明一个InputStream对象os作为当前运行进程的输入流获取和一个大小为256的byte数组。

        若os可以读入数据,则baos写出,如果写出字符串中包含"soffice.bin",说明此时系统里有正在运行的office组件,之后自动结束该进程,并返回true以新进程。

    private boolean killProcess() {
        boolean flag = false;
        Properties props = System.getProperties();
        try {
            if (props.getProperty("os.name").toLowerCase().contains("windows")) {
                Process p = Runtime.getRuntime().exec("cmd /c tasklist ");
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                InputStream os = p.getInputStream();
                byte[] b = new byte[256];
                while (os.read(b) > 0) {
                    baos.write(b);
                }
                String s = baos.toString();
                if (s.contains("soffice.bin")) {
                    Runtime.getRuntime().exec("taskkill /im " + "soffice.bin" + " /f");
                    flag = true;
                }
            } else {
                Process p = Runtime.getRuntime().exec(new String[]{"sh","-c","ps -ef | grep " + "soffice.bin"});
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                InputStream os = p.getInputStream();
                byte[] b = new byte[256];
                while (os.read(b) > 0) {
                    baos.write(b);
                }
                String s = baos.toString();
                if (StringUtils.ordinalIndexOf(s, "soffice.bin", 3) > 0) {
                    String[] cmd ={"sh","-c","kill -15 `ps -ef|grep " + "soffice.bin" + "|awk 'NR==1{print $2}'`"};
                    Runtime.getRuntime().exec(cmd);
                    flag = true;
                }
            }
        } catch (IOException e) {
            logger.error("检测office进程异常", e);
        }
        return flag;
    }

        若当前系统不是windows系统,执行过程与上述语句相似,只不过将获取线程的方法由Runtime.getRuntime().exec("cmd /c tasklist ")换成Runtime.getRuntime().exec(new String[]{"sh","-c","ps -ef | grep " + "soffice.bin"})。毁灭线程的方法由Runtime.getRuntime().exec("taskkill /im " + "soffice.bin" + " /f")换成String[] cmd ={"sh","-c","kill -15 `ps -ef|grep " + "soffice.bin" + "|awk 'NR==1{print $2}'`"}。 

5、public void destroyOfficeManager();

        负责销毁最初创建的OfficeManager对象,防止内存泄露。

         该销毁方法前有一个@PreDestroy注解,被该注解修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法,但是会在destroy()方法之后运行,在Servlet被彻底卸载之前。

        项目结束前运行此方法,这是为了保证线程安全,如果office组件存在且还在运行,就会销毁掉线程。

public void destroyOfficeManager(){
        if (null != officeManager && officeManager.isRunning()) {
            logger.info("Shutting down office process");
            officeManager.stop();
        }
    }
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: window kkfileview启动office组件失败可能有多种原因。首先,可能是由于office组件没有正确安装或配置。通过重新安装office程序或修复现有的安装,可以解决这个问题。其次,kkfileview可能与操作系统或office版本不兼容,需要确保使用兼容的版本。另外,如果操作系统或office有任何更新或修补程序可用,应该安装它们,以确保系统和组件的稳定性。同时,还应检查kkfileview的最新更新,并确保已安装最新版本,以获得最佳兼容性和性能。 ### 回答2: window kkfileview启动office组件失败可能是由于以下几个原因: 1. Office组件没有正确安装:如果您没有正确安装Office组件,或者Office组件的版本与kkfileview不兼容,就会导致启动失败。您可以尝试重新安装Office组件,并确保安装的版本与kkfileview要求的版本匹配。 2. Office组件缺少必要的依赖文件:有时候,Office组件启动失败是因为需要一些必要的依赖文件,例如.NET Framework等,而这些文件可能缺失或损坏。您可以尝试重新安装这些依赖文件,或者通过Office修复工具修复Office组件。 3. 系统设置不正确:某些系统设置也可能导致Office组件启动失败。例如,系统语言设置不正确、注册表项损坏等。您可以尝试调整这些设置或者使用系统修复工具修复相关问题。 4. 内存不足:如果系统内存不足或者被其他应用程序占用过多,也可能导致Office组件启动失败。您可以关闭一些不必要的应用程序,释放系统内存。 如果您尝试了以上方法仍然无法解决问题,建议您联系kkfileview的技术支持或者微软的Office技术支持,寻求进一步的帮助和指导。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值