office XML的交互性
2007-02-23 12:20:54 来源:WEB开发网这是一系列文章中的第一篇,旨在说明与微软Office XML Reference Schemas (WordProcessingML 和 SpreadSheetML)的交互性。 这里, 我们将来分析如何使BEA WebLogic 和 IBM WebSphere (运行于 微软 Windows 或 Linux环境下) 被用来生成服务器端文档,这些文档可以被那些在系统上装有微软Office Word 2003的用户所读取。
适用于:
• | Microsoft .NET Framework 1.1 |
• | Microsoft Visual Studio.NET 2003 |
• | Microsoft Office Word 2003 |
• | BEA WebLogic Workshop 8.1.4 |
• | IBM Rational Application Developer 6.0 |
• | Microsoft Windows XP Professional |
• | RedHat Linux 9.0 |
本文描述了一个出生证明文档创建过程的案例。
案例:创建出生证明为了说明在BEA WebLogic 和 IBM WebSphere环境下与Office XML的交互性,设想如下的场景:
Contoso 注册公司是一家私人公司,得到了政府关于记录出生注册的转包合同。 在他们的业务中包括一种 “walk-in” 办公服务,这样,申请者可以通过提供他们的详细资料以及身份证明进行个人注册。
当前记录这种数据的系统相当简单:注册者完成一份文本格式的、关于出生详细资料的表格。 表格中的信息被录入到应用系统(基于 J2EE, Java 2 平台企业版),并被保存到数据库。 每周一次,数据库的一个快照被送至印刷厂。 印刷厂对数据库中的每条记录生成一份“纪念式的出生证明”,这些证明会邮寄给注册者。
这个系统现在工作得很好,但是Contoso 指望把这个过程流水线化,以节省成本。 在分析后他们发现,厂外印刷开始变得贵了,而且,还不得不处理出生证明在邮寄过程中丢失的事故。
为了解决这些情况,Contoso正在考虑使出生证明文件在注册时就可以被打印出来。 所有的注册处都配备有打印机,而且那里的计算机运行有微软的Windows XP系统以及Office 2003软件。 他们认为这样可以达到节省成本以及为用户提供更好的服务的目的:注册处在注册者进行注册时就可以交付他们打印好的证明。
Contoso 的IT部门启动了一个关于以现在的技术手段是否能够实现上述设想调查,极具挑战性。简而言之, 他们需要从现有的J2EE 应用生成与Office 2003兼容的文档——而且最好不需要任何附加其它的软件和应用。在熟悉了这个领域之后, 这个IT部门在微软Office 2003发现了一种可支持的格式,这就是WordProcessingML。
WordProcessingML微软的 Word 2003 支持一个叫做“另存为 XML文档” 的功能,就是你打开任何 Word 文档, 打开文件菜单,选择另存为…… 然后选择 XML格式。如同你设想的,当前文件将被保存为XML格式的文件。 这种文件使用的就是WordProcessingML格式。
WordProcessingML 是Office 2003 Reference Schemas提供的一种XML方案。它描述了如何将一份 Word 2003 文档以及相关联的部分——比如字形、字体、表格、图形等,诸如此类——以XML文档的形式表现。微软为这些方案提供不需版权的注册和文件。
<w:wordDocument xmlns:w=
'http://schemas.microsoft.com/office/word/2003/wordml'>
<w:body>
<w:p>
<w:r>
<w:t>Hello, World.</w:t>
</w:r>
</w:p>
</w:body>
</w:wordDocument>
一份Word XML文件就是一份存为XML格式的Word文件, 符合WordProcessingML 格式。然而和Word中现有的其它“另存为”格式 (像.txt或 .rtf)不同的是,当一份文件存为XML格式,它保留了它所有的格式、功能、以及编辑能力。 这就是说,一份存为Word XML 的文档具有与原有二进制的.doc 格式文档具有等同功能的优势。
这些导致了有趣情景的性能是以往版本的Office不具备的。 比如,遵循WordProcessingML 方案的XML 文档现在可以在非微软平台 (只要提供能够生成XML的平台即可)上生成。当这个类型的XML文档装载进Office 2003,它会自动被识别为Word文档并被呈递出来。
Contoso使用WordProcessingML现在回到我们前面提到的案例,Contoso的IT部门注意到WordProcessingML 并发现它可以提供好的解决方案。 下面的图表对这个解决方案进行了说明。 Contoso修改了现有的J2EE应用,添加了符合WordProcessingML标准的XML文档生成功能,用以生成XML格式的“纪念性出生证明”文件。然后这些文件被送回至Contoso的现有的使用Office 2003 的客户端。 这些文档可以在Office 2003中打开,打印,并可马上交道注册者手中。
图1.使用 WordProcessingML
如图1中所展示的,注册的详细出生信息在注册处输入终端,并存储在 J2EE服务器上。 服务器将数据存储在数据库中,同时生成WordProcessingML 格式的出生证明文件。然后这份文件在Word 2003中打开并送至本地打印机进行打印。
注意:这个案例(包括样本代码)不是在Java 和(或) J2EE 平台上的应用向导。它的目的是展示在这样一个范例,就是在基于非微软技术的平台上,如何创建Word 2003兼容文档。这里使用的出生证明案例仅用于举例。
安装并使用样本
本文提供的样本代码是针对运行于BEA WebLogic 8.1.4 或者 IBM WebSphere 6.0 (使用 IBM's Rational Application Developer 6.0) 中的情况下设计的。 样本支持运行于Windows 或 Linux操作系统中的 BEA WebLogic 和 IBM WebSphere。样本代码也能运行于其它的操作系统或者软件平台,只是它们并未经过测试。
在开始运行样本之前, 需要按步骤安装数据库,然后选择J2EE 服务器平台。
安装数据库支持前面已经描述过,这个案例读取出生证明信息并写进数据库,而且通过以com.microsoft.samples.officexml.database文件包名称的方式在数据库中修改相关细节,任何JDBC 兼容数据库都可以用来进行出生证明信息的写入。
在本文中,我们将数用IBM's Cloudscape v10.0数据库 (最近捐助给了 Apache 软件基金会并改名为 “Derby”)。当然还可以免费使用另一种支持的数据库——虽然这可能要求在资源代码中作适当的修改。 本文中我们选用IBM's Cloudscape ,用来支持J2EE 应用数据库和绑定到操作系统上。
如果你没有安装Cloudscape数据库,就需要从IBM下载Derby数据库软件。步骤如下:
1. 从Cloudscape install page下载 “Java Installer” 将内容释放至一个临时目录下。
打开目录并下面的命令:
java -jar 10.0-IBM-Cloudscape.jar
将会安装所需文件。 Windows环境下的默认安装路径是C:program filesIBMCloudscape_10.0 Linux 下为/opt/IBM/Cloudscape_10.0,你可以选择喜欢的任意路径。
发布样本代码从http://workspaces.gotdotnet.com/officexmlinterop为你的平台下载包含样本代码的JAR文件。
对于BEA WebLogic,使用下面的命令将内容释放至运行着BEA WebLogic 的服务器上的一个目录中:
jar xvf BEABirthCertSample.jar
对于 IBM WebSphere,使用相同命令释放IBM 特定版本的样本:
jar xvf IBMBirthCertSample.jar
从这个样本出发,我们假设所有JAR文件的内容会放置于这样一个目录中 c:officexmlBirthCert (Windows环境) and /usr/officexml/BirthCert (Linux环境)。 当然你可以选择其他路径,但这会使样本安装步骤做相应调整。
JAR文件中释放出来的目录包括:
/database
样本所需的数据库。
/docs
用于存储服务器上生成的XML文件。
/images
用于存储证明文件的签字图像。
/project
包含服务于JSP 客户和网络服务的项目文件。
/schema
包含一个XSD方案用于网络服务的再次致电功能。
/template
包含用于出生证明文件的XML 文件模版。
启动数据库在数据库的子目录(c:officexmlBirthCertdatabase or /usr/officexml/BirthCert)中运行 database.bat 文件 (database.sh——Linux环境)。 将会编译并运行所需文件,对样本提供数据库支持。如果你选择了其他路径来安装 Cloudscape 数据库,你需要相应地修改脚本。
现在开始安装数据库并发布样本代码,根据你的平台参考下面的部分。
在 BEA WebLogic 8.1.4中运行样本此样本代码已经在如下操作系统:微软Windows XP 专业版、Windows Server 2003以及Red Hat Linux 9.0的BEA WebLogic Workshop 8.1.4 (8.1 SP4)中测试过。理论上样本代码可以在以上操作系统的其他版本中运行,只是并没有经过测试。
编译样本代码
在BEA WebLogic Workshop中打开需要的项目,在项目所在目录中双击BirthCert.work 文件。如果是第一次打开文件,系统会提示你定义一个应用服务器,选择 OK 确定服务器路径。 已提供的所有配置范例都可以运行样本代码或者你可以通过向导来创建自己的配置文件运行样本代码。
图2.查看出生证明样本(BirthCertSample)
你将会注意到BEA WebLogic Workshop 中显示了3个项目——BirthCertSchema (用XSD schema中表示一份出生证明的项目); BirthCertWebClient (在JSP客户端的项目);还有BirthCertWebService (Web服务项目)。
在BirthCertificateWebService项目中打开 BirthCertificate_en_US.properties文件。 这个文件可以在 com/microsoft/samples/officexml/properties 目录中找到。
在这个文件中,你可以发现指向范例应用目录的三个属性。
WordMLXSL=/officexml/BirthCert/template/BirthCertificate.xsl
ImageFolder=/officexml/BirthCert/images/
WordMLDocFolder=/officexml/BirthCert/docs/
注意:如果你在使用Linux操作系统的主机上运行样本,则需要在每个此类路径的开始处添加(或在本地进行修改)/usr。
WordMLXSL属性用XSL文件的定位。 ImageFolder还有 WordMLDocFolder 代表了输出图像和文档的位置。
如果你使用了其他非默认目录,相应地改变上面的默认路径进行文件保存。
添加所需的库支持
另外,你需要在BEA WebLogic Workshop中给项目添加三个库,以支持样本代码正常运行。 其中两个是数据库支持所需要的。 另外一个是通过JSP页面上传文件所需要的。 在WebLogic Workshop IDE中右键点击库文件夹并选择添加库。
对于这些库结构,引导至Cloudscape 安装的库文件目录 (默认为C:program filesIBMCloudscape_10.0 或/opt/IBM/Cloudscape_10.0) 并选择db2cc.jar和 db2jcc_license_c.jar 文件。
你需要一个名为commons-fileupload-1.0.jar 的库用于支持上传功能的实现。 它是来源于Apache Jakarta项目的通用库的一部分。如果你本地没有这个JAR文件,可以从The Jakarta Project Web site获取。
启动应用服务器
当所需的库加载完毕后,在构建(Build)菜单选择 构建应用(Build Application) 进行应用程序的编译。
一旦建立,从工具/WebLogic Server菜单中选择开始 WebLogic Server 。这将启动应用服务器。
在默认情况下,应用程序将会自动部署到服务器上。 在这个案例中不是这样执行的,你可以鼠标右键点击BirthCertSample项目,选择Deployment和Deploy进行手动部署。
针对IBM WebSphere 6.0进行样本安装此样本代码已经在如下操作系统:微软Windows XP 专业版、Windows Server 2003以及Red Hat Linux 9.0的BEA WebLogic Workshop 8.1.4 (8.1 SP4)中测试过。理论上样本代码可以在以上操作系统的其他版本中运行,只是并没有经过测试。
编译样本代码
在IBM Rational Application Developer 6.0打开所需项目,打开IDE并选择 项目 文件夹位置作为默认工作区。 如果你将样本代码安装至默认目录,将会是c:officexmlBirthCertproject (Windows )或者 /usr/officexml/BirthCert/project(Linux)。
第一次打开IDE会创建一个新的工作区。从文件菜单中选择输入……将项目输入工作区。选择项目交换(Project Interchange) 然后 选择 下一步。 在项目目录下挑选 项目.zip 文件并选中所有5个可用项目。 点击结束按钮将这些项目输入工作区。
图3.导入项目交换
在输入后你会发现在IDE中显示有5个项目——客户端程序和Web服务 项目列于动态Web项目文件夹下。 与这些项目中每一个相应的是一个个EAR 项目 (分别为ClientEAR 和WebServiceEAR)。最后,一个 Schema项目被用来从一个包含出生证明样本数据类型的XSD文件中生成的Java Beans。
在WebService项目中打开 BirthCertificate_en_US.properties 文件。 这个文件可以在处于Java Resources / JavaSource 目录下的com/microsoft/samples/officexml/properties目录中找到。
在这个文件中,你可以发现指向范例应用目录的三个属性。
WordMLXSL=/officexml/BirthCert/template/BirthCertificate.xsl
ImageFolder=/officexml/BirthCert/images/
WordMLDocFolder=/officexml/BirthCert/docs/
WordMLXSL属性用XSL文件的定位。 ImageFolder还有 WordMLDocFolder 代表了输出图像和文档的位置。
如果你使用了其他非默认目录,相应地改变上面的默认路径进行文件保存。
添加所需的库以支持代码样本的运行
另外, 你需要在IBM Rational Application Developer中给项目添加三个库结构。 其中两个是数据库支持所需要的。 另外一个是通过JSP页面上传文件所需要的。 在WebLogic Workshop IDE中右键点击库文件夹并选择添加库。
于这些库结构,右键点击WebService项目并选择属性。 在属性窗口中,选择 Java 建立路径并点击添加外部JARs……按钮。引导至Cloudscape 安装的库文件目录 (默认为C:program filesIBMCloudscape_10.0 或/opt/IBM/Cloudscape_10.0) 并选择db2cc.jar和 db2jcc_license_c.jar 文件。
点击OK保存改变。现在在WebService 项目中引导 WebContent -> WEB-INF -> lib 目录。右键点击此文件夹并选择输入。 选择文件系统, 再一次浏览Cloudscape安装的库文件目录并选择相同的两个JAR 文件(db2cc.jar和db2cc_license_c.jar)。 将这些JAR文件输入到这个库文件目录下。
你需要一个名为commons-fileupload-1.0.jar 的库用于支持上传功能的实现。 它是来源于Apache Jakarta项目的通用库的一部分。如果你本地没有这个JAR文件,可以从 The Jakarta Project Web site获取。
要将这个JAR文件添加至你的项目中,右键点击 客户端项目并选择属性。 在属性窗口中,选择Java 构建路径并点击添加外部JARs……按钮。 引导至含有commons-fileupload-1.0.jar的目录并把它添加至建立路径上。 点击OK保存改变。
现在在WebService 项目中引导 WebContent -> WEB-INF -> lib 目录。右键点击此文件夹并选择输入。 选择文件系统, 再一次浏览commons-fileupload-1.0.jar文件目录并选择将此添加至这个库文件目录下。
启动应用服务器
一旦添加了这些输入应该将此项目添加至一个实现配置好的IBM WebSphere 6.0 应用服务器 (可提供良好的测试环境)实例下。如果事先没有创建这种实例 应该参考IBM文档进行建立。
要部署这个应用, 右键点击服务器并选择添加/删除项目。 选择ClientEAR 和 WebServiceEAR 部署并添加它们。 在服务器上执行一次发表操作可以部署这两个应用。
运行样本代码
随着应用的部署完成以及数据的运行,现在可以进行样本应用的测试了。
首先打开浏览器窗口:
http://servername:7001/BirthCertWebClient (BEA WebLogic)
http://servername:9080/Client (IBM WebSphere)
注意 用你的服务器的真名替换服务器名称并进行调试。 如果在同一台机器上运行可使用本地主机。
随后出现的菜单屏如下图:
图4.创建一份出生证明
点击 创建一份出生证明(Create a Birth Certificate)按钮。 这将展示出生证明细节的入口屏:
图5.完成出生证明信息
在此表内,输入一些注册者出生细节信息。 包括全名、日期、出生地。 注意你还可以上传父母的签字文件。 点击恰当的链接可浏览文件。
当出生证明数据输入后,点击保存按钮。 如果成功你会看到提示信息并返回主菜单。 如果出错—可能是数据或安装的问题—同样会得到提示。
出生证明数据现在未被输入。当返回主菜单时,选择打印出生证明(Print a Birth Certificate)。下面第二个JSP页面:
图6.搜索出生证明信息
输入搜寻所需信息。 至少需要姓氏, 中名(如果提供了), 名字, 出生日期。 (这些信息需与你之前输入的信息一致)。 点击搜索按钮提交搜索。
如果搜索成功,网络应用会生成相应的WordProcessingML格式出生证明。 浏览器会打开两个窗口—一个是规范XML格式表述的文档 ,另一个是WordProcessingML 文档。
对于 WordProcessingML 文档,你会得到看的提示对话框。 注意对话框会将它自动识别Word文档。
图7.下载Word格式的出生证明文档
在Word有两种方式查下载的出生证明文档: 如果安装了Word 2003,点击打开按钮—会同过浏览器中嵌入的Word进行展示。 或者,选择保存, 把文件保存之一个文件夹中, 然后在使用Windows Explorer 再次打开。
打开后看到的文档如下:
图8.在Word中查看成生证明文档
这是J2EE应用服务器生成的Word文档。 虽然它以XML格式返回文件, 但文件中的文档类设置允许它直接以Word格式打开。
在我们的案例中,可以设想文件如何生成并送到本地打印机打印出来。
样本代码如何工作已经指出,样本的工作机制是使用一系列 JSP页面 (Java服务器页面)收集使用者的资料, 将信息存入服务器中并取回,先后生成相应的WordProcessingML XML文档。 这部分将分步进行描述。
创建出生证明记录在 BirthCertWebClient 项目中, 在jsp 的根下, BirthCertHomePage.jsp 用来表示“创建和搜寻” 初始菜单。 当使用者创建一份新的出生证明时, BirthCertCreateForm.jsp 页面用来收集详细资料。 UploadMotherImage.jsp和UploadFatherImage.jsp 分别从文档中获取任何母亲和父亲的签字图像。
在以表格形式提交信息之上,还可以访问同一台机器中运行着的出生证明的服Web务 。这个服务负责数据库输入信息并生成最终的WordProcessingML文档。 在JSP页面中 , 这些访问通过在 com/microsoft/samples/office/handler中的RequestHandler 服务程序完成。
为创建出生证明, 网络服务系统 (BirthCert.jws)提出了名为createBirthCertificate 的方式。 这种方式接受在XSD (可在项目中的Schemas 文件夹中找到)中预定义过的出生证明文档。这种方式调用时, AccessBirthCertData将用来向数据库。 完成时,Web服务发出成功报告,使用者返回主菜单。
搜寻出生证明记录进行搜寻, 将从主菜单导向BirthCertSearchForm.jsp页面。 再这个页面使用者输入搜寻标准。然后进入网络服务,再一次通过RequestHandler 服务程序。Web服务为此提出一个名为searchBirthCertificate的方式。这种方式接受在XSD (同样可在项目中的Schemas 文件夹中找到)中预定义过的出生证明搜寻文档。
通过这些数据, 网络服务在数据库中搜寻符合记录, 每发现一个符合的记录, 就会生成一份 WordProcessingML 文档。
生成微软Word 文档生成一份符合WordProcessingML 方案的 XML文档有几种方法。
一种方式是通过暂存方式建立XML文件。 这给了文件极大的适应性, 但是需要一个很长的过程去手写全部代码。 一个有着很多字型和样式的复杂的文档A会造成很多复杂的代码。
另一种方式是在Word中创建一个模板,保存为XML格式特定的字段(例如###NAMEFIELD###)形式标明。 然后可以在XML 剖析器中加载,搜寻合适的字段(###NAMEFIELD###), 然后以真实值替代。 这种方式更有效率,但仍有可能出错 (例如, 文档中有的文本与字段名冲突)。
样本中使用第三种方式,就是XSL (XML Stylesheet) 文件用来生成最终的WordProcessingML 文档。
图9.使用XSL (XML Stylesheet) 生成WordProcessingML 文档
如上图所示, 一份纪念出生证明的模板存为XML格式。文件改名为XSL并用XSL标签替换样本数据。 例如, 出生证明中孩子名字的位置以下面的标签进行替换:
<xsl:template match=“BirthCertificate/ChildName”>
可以在模板文件夹调查XSL文件去看所有的字段。
当网络服务从数据库中调取数据后,它建立一份XML 文档(内存中) 代表出生证明数据。 比如:
<BirthCertificate>
<ChildName>John Doe</ChildName>
<Sex>Male</Sex>
<BirthDate>12-05-2005</BirthDate>
...etc...
</BirthCertificate>
应用了XML的转换—使用XSL模板并在XML串中应用数据。在BEA WebLogic 和IBM WebSphere环境下使用XSL 库结构就完成了, 但不能使用 XSLT兼容处理器。
// Create a new instance of the transformer factory
TransformerFactory tf = TransformerFactory.newInstance();
StreamSource xslStream = new StreamSource(xslFile);
Transformer t = tf.newTransformer(xslStream);
// Create the output file
StreamResult xmlOutputResult = new StreamResult(filename);
// Perform the transform
t.transform(ds, xmlOutputResult);
WordProcessingML 文档最终交给使用者。
为确保最终XML文件可以被客户端识别为 WordProcessingML 文档,我们在 XSL文件中加入下面的XML 预处理首标:
<xsl:processing-instruction name=“mso-application”> progid=“Word.Document”</xsl:processing-instruction>
这样,XML首标可以使XML文档被Word识别。
处理文档中的图像你可能注意到在样本中, JSP页面同样支持上传图像 (以使签字可以附在纪念出生证明文件中)。
为此, 图像上传依靠 JSP 使用文件上传控制。当使用网络服务时, 图像存储为本地文件(在项目目录的 图像文件中)。 图像名称与出生证明的ID 一致(例如, 22_Father.bmp 是22号的父亲的签字)。
向服务器生成并返回的 WordProcessingML 文档中插入图像相当容易。使用一个64位解码器将二进位图像转为64位,并加入文档模型中。 XSL 模板含有将64位数据与Word文档之间所必要的转换:
<xsl:template match="BirthCertificate/FatherSignature">
<ns0:FatherSignature><w:tc><w:tcPr><w:tcW w:type=
"dxa" w:w="4428"/></w:tcPr><w:p><w:pPr><w:jc w:val=
"center"/></w:pPr><w:r><w:t><w:pict>
<w:binData w:name=http://tech.ddvip.com/2007-02/"wordml://03000002.png">
<xsl:apply-templates/>
</w:binData><v:shape id="_x0000_i1025" type=
"#_x0000_t75" style="width:115.6pt;height:144.65pt">
<v:imagedata src=http://tech.ddvip.com/2007-02/"wordml://03000002.png"
o:title="Sample2"/></v:shape></w:pict></w:t></w:r></w:p></w:tc>
</ns0:FatherSignature>
</xsl:template>
如你所见, 64位图像通过Word文档元素 w:binData 成功插入。 A暂时文件名 (wordml://0300002.png) 以及标题用来加入文档参考中。
返回Word文档当网络服务创建了文档, 文件名返回至命名的JSP页面。 命名一个新的JSP页面 (ViewWordML.jsp)。 它从磁盘中读取文件, 正确设置内容类型(text/xml), 并通过JSP 输出串返回内容。
这将使浏览器为用户弹出 “打开/保存/取消” 对话框 ,可以进行Word 文档的打开或保存。
结论本文说明了在BEA WebLogic 或者 IBM WebSphere 中WordProcessingML 怎样生成一个Word 2003兼容文档。目的是展示Office XML Schema 格式的适应性以及怎样在非微软平台上创建这些类型的文档。
读完本文后你也许会想: “可以用 PDF做一样的事情。” 对于这个特定的案例, 是这样的。 然而, Office XML schemas 的应用可以展现一些新的令人激动的情景。比如,用户编辑存储文档并存为XML格式。 这就使再把文档送至服务器-提供循环支持和文档的简单工作流的形成成为可能。
比如, 一种应用创建了一个文档,提示用户填写细节信息(比如,填写一份支出报告)。 用户可以填写要求的信息, 存为XML格式, 然后提交文件至服务器。 服务器执行“XML diff” 对比原始文件, 找出差别—并有效地解析出用户输入的内容。 当你发现这些可能性, 使用 WordProcessingML schema 处理XML 文件的威力就显而易见了。
在此向这些在编码工作过程中提供的帮助的人表示感谢:Anurag Katre, Pramod Pawar, Nilesh Jain 和 Hemlata Jadhwar (Tata Consulting Services)。 同时也向 Jean Paoli, Joe Andreshak, Pascal Stolz (Microsoft Corporation), 还有Phillip Conrad (University of Delaware)对他们的支持表示感谢。
关于作者
Simon Guest 是一位 项目经理,就职于微软公司的架构策略(Architecture Strategy)团队, 专攻交互性与集成的研究。 Simon拥有伦敦Westminster大学信息安全技术专业硕士学位, 并且是Microsoft .NET 以及 J2EE 交互性工具包的作者 (Microsoft 2003.9发布)。
可通过作者的博客与他联系 http://www.simonguest.com。
更多精彩
赞助商链接