目的

用户上传DOC,DOCX等文件格式后,能够在线查看到文件的内容。

想法

WORD文件内容是富文本,可以转换成HTML格式后表现出来,或者转换成PDF格式,然后通过pdf.js来实现在线阅读。

实现

由于后端环境是Windows,脚本语言为PHP, 因此可以使用PHP的COM组件来调用OpenOffice 或者 MS Word 来实现该格式转换问题。

准备

1. 安装OpenOffice 或者 MS Word 程序

OpenOffice可以在这里下载

2. 打开COM

注意:COM组件只试用于Windows版本下的PHP,需要PHP5及.Net runtime.

PHP 5.3.15/5.4.5 需在php.ini手动打开php_com_dotnet.dll,此前版本默认已经打开。

另外,您可能还需要打开com.allow_dcom, 具体配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extension=php_com_dotnet.dll

...

[COM]

...

; allow Distributed-COM calls
; http://php.net/com.allow-dcom
com.allow_dcom = true

; autoregister constants of a components typlib on com_load()
; http://php.net/com.autoregister-typelib
com.autoregister_typelib = true

使用MS Word来转换

PHP代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
try{
$word = new COM("word.application") or die("Unable to instanciate Word");
echo "Strat...\n";
// set it to 1 to see the MS Word window (the actual opening of the document)
$word->Visible = 0;
// recommend to set to 0, disables alerts like "Do you want MS Word to be the default .. etc"
$word->DisplayAlerts = 0;
// open the word 2007-2013 document
$word->Documents->Open(realpath("in.docx"));
// save it as word 2003
// $word->ActiveDocument->SaveAs('newdocument.pdf');
// convert word 2007-2013 to PDF
$word->ActiveDocument->ExportAsFixedFormat("F:\\Tools\\Program Handle\\WAMP\\www\\wordConvertor\\output.pdf", 17, false, 0, 0, 0, 0, 7, true, true, 2, true, true, false);
// quit the Word process
$word->Quit(false);
// clean up
unset($word);
echo "Done!";
}catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
?>

如果您遇到以下类似的错误, 并确认MS Word已经安装好:

1
2
PHP Fatal error: Uncaught exception 'com_exception' with message 'Source: Microsoft Word
Description: This command is not available because no document is open.'

请尝试这样解决(建议先尝试方法二):

方法一:

  1. 开始
  2. Win+R 运行
  3. 输入 dcomcnfg
  4. Press OK
  5. 展开 Component Services
  6. 展开 Computers
  7. 右键 My Computer,点击”Properties”
  8. Default Properties选项卡
  9. 确认勾选Enable COM Internet Services on this computer 和Enable Distributed COM
  10. 设置 Default Authentication Level 为 Default
  11. 设置 Default Impersonation Level 为 Identify
  12. COM Security tab 选项卡
  13. Access Permissions area, 点击 Edit Limits…
  14. 确认ANONYMOUS LOGIN 下允许 remote access
  15. 点击 Add…
  16. 在 “Enter object names to select” 文本框内输入 “Everyone”
  17. 允许Everyone 有 Local Access 和 Remote Access
  18. 点击确认
  19. 同样在Access Permissions 区域,点击Edit Default
  20. 确认允许 SELF and SYSTEM 有 remote access
  21. 点击 Add...重复步骤 16 and 17
  22. 点击确认
  23. 在Launch and Activation Permissions区域下点击Edit Limits...
  24. 确认允许Administrator user 有 remote launch 和 remote activation的权限
  25. 如果Everyone用户组在这里不存在,请重复步骤16,17
  26. 允许Everyone group有local launch, remote launch, local activation,和 remote activation权限.
  27. 点击确认
  28. 同样在Launch and Activation Permissions区域下,点击Edit Default...
  29. 确认Administrator, INTERACTIVE, 和 SYSTEM用户组有 remote launch 和 remote activation 的权利.
  30. 重复步骤16 和 17 以创建Everyone用户组
  31. 允许Everyone group有local launch, remote launch, local activation,和 remote activation权限.

ci_dcomcnfg_1

同时,确认php.ini中的com.allow_dcom已经打开。重启后再试,如果问题依旧,请继续以下步骤:

  1. 依次点击Console Root -> Component Services -> Computers -> My Computer -> DCOM Config, 在此处找到Microsoft Word 97 - 2003 Document(如果没有找到,请尝试方法二).
  2. 右键属性.
  3. Security 选项卡中确认Launch and Activation PermissionsAccess PermissionsConfiguration permissions已经勾选为Default.
  4. Identity选项卡中选择The interactive user或者指定一个Admin用户, 通常它可能是The launching user.

方法二:

  1. Win + R 打开运行;
  2. 输入mmc -32 ,按下OK;
  3. 在新弹出的窗口中点击 File -> Add/Remove Sanp-in (或者Ctrl + M);
  4. 在新窗口中的左侧找到Component Services, 添加,确认;
  5. 依次点击Console Root -> Component Services -> Computers -> My Computer -> DCOM Config, 在此处找到Microsoft Word 97 - 2003 Document.
  6. 右键Microsoft Word 97 - 2003 Document,选择Property, 在Identity选项卡中选择The interactive user或者指定一个Admin用户, 通常它可能是The launching user.

ci_dcomcnfg_2

使用OpenOffice来转换

启动OpenOffice服务

两种方式,方式一比较简便。

确认是否已经开启参考以下命令来获取回复:

netstat -nao|findstr -c":8100"

其中8100应该改为您自行设定OpenOffice服务端口。

方式一

假设您的OpenOffce安装在C:\Program Files (x86)\OpenOffice 4\program\soffice.exe,在CMD中输入以下命令:

1
"C:\Program Files (x86)\OpenOffice 4\program\soffice.exe" -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard

方式二

  1. 创建文件夹在该路径上 C:\utils, 把srvany.exe复制到里面去. 该文件从Windows Server 2003 Resource Kit提取.

  2. 打开CMD(管理员模式). 运行一下命令.

1
2
3
C:\Windows\system32>sc create "ooservice" binPath= "\"C:\Utils\srvany.exe\"" DisplayName= "OpenOffice Server" start= auto

[SC] CreateService SUCCESS
  1. 打开注册表编辑器(Win + R, 输入regedit). 寻找到HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\ooservice.

  2. 创建一个新的 key,名为 Parameters.

  3. Parameters 这个Key中, 创建一个名为 Application的String,其值如下:

1
2
"C:\Program Files (x86)\OpenOffice 4\program\soffice.exe" -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard
Note: Update path to open office as necessary.
  1. 最后您的注册表大概类似于这个样子:

  1. Control Panel -> Administrative Tools -> Services 中找到Open Office Server并开启.

  2. 使用上述的netstat -nao|findstr -c”:8100”命令确认是否已经开启.

成功后回复如下:

1
2
3
C:\Windows\system32>netstat -nao|findstr -c":8100"

TCP 127.0.0.1:8100 0.0.0.0:0 LISTENING 6016

PHP代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

function MakePropertyValue($name, $value,$osm){
$oStruct = $osm->Bridge_GetStruct("com.sun.star.beans.PropertyValue");
$oStruct->Name = $name;
$oStruct->Value = $value;
return $oStruct;
}

function word2pdf($doc_url, $output_url){
// Invoke the OpenOffice.org service manager
$osm = new COM("com.sun.star.ServiceManager") or die ("Please be sure that OpenOffice.org is installed.\n");
// Set the application to remain hidden to avoid flashing the document onscreen
$args = array(MakePropertyValue("Hidden",true,$osm));
// Launch the desktop
$top = $osm->createInstance("com.sun.star.frame.Desktop");
// Load the .doc file, and pass in the "Hidden" property from above
$oWriterDoc = $top->loadComponentFromURL($doc_url,"_blank", 0, $args);
// Set up the arguments for the PDF output
$export_args = array(MakePropertyValue("FilterName","writer_pdf_Export",$osm));
// Write out the PDF
$oWriterDoc->storeToURL($output_url,$export_args);
$oWriterDoc->close(true);
}

try{
echo "Strat...\n";
$doc_file = 'file:///F:/Tools/Program Handle/WAMP/www/wordConvertor/in.docx' ;
$output_file = 'file:///F:/Tools/Program Handle/WAMP/www/wordConvertor/out.pdf';
word2pdf($doc_file, $output_file);
echo "Done!";
}catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}

?>

问题及注意

1.程序长时间卡在new COM("com.XXXXX")这一行

由于实际上是调用MS WORD或者OpenOffice来完成的功能,因此调试时,可以利用任务管理器查看相关进程,防止因为错误的代码/环境重复打开多个进程而得不到关闭,导致后面的测试也出错。

此外,还可以调短php.ini中的max_execution_time,个人感觉3s内无响应就该去再做检查了.

参考

https://www.getfilecloud.com/supportdocs/display/cloud/Running+Openoffice+as+a+service+in+Windows

http://www.figured-it-out.com/figured-out.php?sid=24

http://stackoverflow.com/questions/12104775/cant-open-word-doc-with-com-in-php