1. 程式人生 > >Office Open XML 文件格式(轉)

Office Open XML 文件格式(轉)

Office Open XML文件格式的詳細說明以及規格是在2006年歐洲計算機制造商協會批准的一項標準, 編號是Ecma376, 到發稿時為止已經通過了ISO國際標準化組織的評審成為了一項國際標準ISO/IEC 29500:2008. 你可以下載Office Open XML的詳細說明文件, 地址在http://openxmldeveloper.com, 當然這個站點上還有許多其他的優秀線上資源. 我們這篇文章的目的是讓你瞭解一下這種文件格式.

Office Open XML 文件格式的初衷

==================

過去, 想要書寫或者部署能夠讀寫, 修改, 生成可以被Microsoft Office應用程式套裝使用的文件的伺服器端應用程式是很有挑戰性的. 那是種很老的二進位制文件格式, 它是在1997年引入的, 一直到Microsoft Office 2003版本一直是Office預設的文件格式. 經驗表明這個二進位制文件格式已經被證明對於絕大多數公司都是非常難以直接操作的, 因為一個程式碼中小小的錯誤就會使得生成或者修改過的Office文件整個崩潰損壞. 很大一部分的讀寫2003版Office文件的生產應用程式通過宿主應用程式的物件模型來操縱文件.

使用應用程式物件模型的, 比如Word, Excel, 自定義的應用程式和元件在desktop上執行比在伺服器端環境下執行要更好. 任何一個花時間寫過這種在伺服器端讓客戶端應用程式平穩執行的額外架構程式碼的人都會告訴你, 這是一個高難度的活兒, 因為諸如Word, Excel這一類的客戶端應用程式從來就沒有被設計為在伺服器端執行. 每當他們遇到一個需要人機互動的模態對話方塊時, 他們都需要一個自定義的工具程式來終結和重啟這些客戶端應用程式.

伺服器端應用場景更加需要的是一種能力, 一種可以讀寫文件而不需要通過宿主應用程式物件模型的能力. Microsoft Office 2000和Microsoft Office 2003引入了一些適度的, 使用XML來為Excel工作簿和Word文件建立內容的能力. 這些進步引入了通過XML解析器來修改文件的一部分的可能性, 這裡的XML解析器可以是包含在.NET Framework中的, 由System.XML名稱空間提供的哦.

通過2007 Microsoft Office System, 微軟通過採用可以被word, excel, 還有powerpoint使用的Office Open XML文件格式的方式, 帶著這個想法走的更遠了. Office Open XML 文件格式對於WSS和MOSS系統的開發人員來說, 來說是一項令人振奮的進步, 因為這些文件格式提供了在伺服器端讀, 寫, 生成word, excel, powerpoint文件的能力, 而不需要web伺服器執行desktop的應用程式.

Word 2007 文件技術內幕

=================

讓我們先檢查一下一個使用Office Open XML文件格式的簡單Word文件的結構吧. Office Open XML文件格式是基於標準ZIP技術之上的. 任何一個頂層水平的文件都被儲存為一個ZIP壓縮包, 這意味著你可以像開啟其他ZIP檔案一樣來開啟Word文件, 然後使用內嵌入Windows Exlorer中的ZIP檔案的支援能力來窺探一下文件的內部結構.

你應該注意到2007 Microsoft Office應用程式套裝, 比如說Word和Excel, 為使用新文件格式的文件引入了新的副檔名. 舉個例子, 使用Office Open XML格式儲存的Word文件的副檔名為.docx, 而老的大家都比較熟悉的.doc副檔名繼續用來描述使用老的二進位制格式儲存的Word文件身上.

一旦Word2007被安裝上, 你就可以開始建立一個新的Word文件, 新增點文字"Hello World". 使用預設的文件格式儲存文件, 檔名為Hello.docx, 然後關閉Word. 下一步, 使用Windows Explorer在檔案系統中找到Hello.docx. 把它重新命名為Hello.zip. 這使得Windows Explorer可以把這個檔案識別為ZIP包. 你現在可以開啟Hello.zip包了, 然後可以看到有Word建立的檔案和資料夾結構. 如下圖:


圖表1. 一個docx檔案被命名為ZIP後, 檢視裡面包含的部分和檔案項

在快速瀏覽了.docx檔案的內部結構之後, 現在是時間介紹一下應用Office Open XML文件格式的檔案中涉及到的一些基本概念和術語了. 頂級的檔案(比如說Hello.docx)被叫做package(包). 因為包(package)是被實現為一個標準ZIP包的, 它自動地提供了對文件的壓縮, 還有供Windows平臺和非Windows平臺的工具程式和API即時地讀取文件中內容的能力.

在包中有兩種內部的元件: parts和items. 總的來說, parts包括文件內容和一些含有用來描述parts的元資料的items. Items可以被進一步的細分為relationship items和content-type items. 我們現在就更深入地討論一下每一種元件的細節吧.

part是包含序列化了的內容的包(package)內元件. 多數的part是些簡單的, 根據相關聯的XML schema序列化為XML的文字檔案. 然而, parts還可以在必要的時候被序列化為二進位制資料, 比如說當一個word文件包含一個圖片或者媒體檔案的時候.

一個part通過統一資源標示符來命名的, 由相對於包的相對路徑和part檔案的檔名組成. 舉例, Word文件的包內首要part叫做/word/document.xml. 下面的列表展現了一些典型的part的名字, 你可以在簡單的word文件的包內找到它們.

/docProps/app.xml
/docProps/core.xml
/word/document.xml
/word/fontTable.xml
/word/settings.xml
/word/styles.xml
/word/theme/theme1.xml

Office Open XML文件格式使用relationships來定義一個源part和一個目標part之間的關係. package relationship定義一個part與頂級包之間的關係. part relationship定義一個父part和子part之間的關係.

Relationship很重要, 因為它們使得這些關聯關係可以被發現, 且不需要檢查和問詢parts中的內容. Relationship是獨立於具體內容的schema的, 所以處理起來就更快. 額外的好處是你可以建立一種兩個part之間的關係, 而不需要修改這兩個part中的任何一個.

Relationship是定義在一種內部元件relationship item中的. relation item在包中像一個part一樣的被儲存, 然而relationship item並不會真正地被看做是一個part. 處於一致性的考慮, relationship items總是被建立在一個命名為_rels的資料夾當中.

For example, a package contains exactly one package relationship item named /_rels/.rels. The package relationship item contains XML elements to define package relationships, such as the one between the top-level package for a .docx file and the internal part /word/document.xml.

舉例, 一個包里正是包含一個包的relationship的item, 叫做/_rels/.rels. 這個package relationship item包含用來定義package relationship的XML元素, 諸如.docx檔案對應的頂級包與你哥內部part /word/document.xml之間的關係.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="../package/2006/relationships ">
  <Relationship Id="rId1"
                Type="../officeDocument/2006/relationships/officeDocument"
                Target="word/document.xml"/>
</Relationships>

正如你所見, 一個Relationship元素定義了一個名字, 型別, 還有一個目標part. 你應該也觀察到了relationship的型別的名字是用跟建立XML名稱空間一樣一樣的約定定義的.

除了單個的package relationship item, 一個包還可以包含一個或多個part relationship items. 比如說, 你定義了/word/document.xml與包內的位於URI /word/_rels/document.xml.rels中的child parts之間的關係. 注意, 在一個part relationship item中, relationship的目標屬性(target attribute)是一個相對於parent part, 而不是頂級包的URI.

每一個包中的part都被使用具體的content type術語來定義. 不要把這些content type與WSS定義的contet type混淆了, 因為這兩個是完全不同的. 包中的content type是定義了part的媒體型別, 子型別, 還有一系列可選引數的元資料. 任何在包中使用的content type都必須被顯式地定義在一個叫做content type item的元件之中. 任何的包都有隻一個content type item叫做/[Content_Types].xml. 下面就是一個典型的Word文件中, 在/[Content_Types].xml內部定義content type的例子.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default
     Extension="rels"
     ContentType="application/vnd.openxmlformats
                  package.relationships+xml"/>
  <Default
     Extension="xml"
     ContentType="application/xml"/>
  <Override
   PartName="/word/document.xml"
   ContentType="application/vnd.openxmlformats
                officedocument.wordprocessingml.document.main+xml "/>
</Types>

Content types被包的使用者(消費者)用來解釋如何讀取和渲染包裡parts的內容. 正如你在前面的列表中看到的, 一個預設的content type典型地與一個副檔名聯絡起來, 比如.rels或者.xml. override content type是用一個不同於預設的與它的副檔名關聯的content type的方式定義一個具體的part. 比如說, /word/document.xml 與override content type相關聯, 而override content type跟預設的content type不同, 預設的content type跟一個.xml副檔名聯絡在一起.

生成你的第一個.docx檔案

=================

儘管已經存在一些程式庫可以用來讀或者寫ZIP檔案包, 你還是應該選擇與.NET Framework 3.0一起釋出的作為WindowsBase.dll一部分的新的打包API, 因為這裡的打包API可以識別Office Open XML文件格式. 比如說, 某個方便的方法可以使得新增一個relationship元素到relationship item中更容易, 使得向content type item中新增一個content type元素更容易. 打包API讓事情變得更簡單, 因為你永遠不需要直接地觸及relationship item 或者content type item.

對於開發WSS3.0, 這裡的打包API有一個優點, 那就是這些API依賴於.NET 3.0 Framework. 你可以肯定WindowsBase程式集和打包API在任何執行著WSS3.0和MOSS的web伺服器上都是可用的.

為了開始在Microsoft Visual Studio 2005工程中應用這些打包API程式設計, 你需要新增對WindowsBase程式集的引用, 如下圖所示.


圖表2.  新增一個WindowsBase程式集的引用, 以便開始對新的打包API程式設計

讓我們通過建立一個簡單的控制檯程式, 讓它生成一個Office Open XML文件格式的.docx檔案作為開始吧.

組成打包API的類都包含在System.IO.Package名稱空間下. 當你在包上工作時, 你同時也要經常地與老的, 熟悉的在System.IO和System.Xml名稱空間下的類打交道. 看看下面的程式碼吧, 它呈現了建立一個新的包的骨架.

using System;
using System.IO;
using System.IO.Packaging;
using System.Xml;

namespace HelloDocx
{
    class Program
    {
        static void Main()
        {
            // (1) create a new package
            Package package = Package.Open(@"c:\Data\Hello.docx",
                              FileMode.Create,
                              FileAccess.ReadWrite);
            // (2) WRITE CODE HERE TO CREATE PARTS AND ADD CONTENT
            // (3) close package
            package.Close();
        }
    }
}

System.IO.Packaging名稱空間包含Package類, Package類暴露出了一個叫做Open的共享方法, 該方法可以被用來建立新的pakcage或者開啟已經存在的package. 如同其他的許多處理檔案IO的類一樣, 對Open方法的呼叫永遠都應該有一個Close方法來配對作為結束.

一旦您建立了新的包, 下一步就是定位一個或多個parts然後將內容序列化到這些parts中. 在我們的下一個例子中, 我們順著官方的"hello world"應用程式來操作, 官方的hello world程式還需要建立一個單個的叫做/word/document.xml的part. 你可以通過呼叫一個開啟的Package物件的CreatPart方法建立一個part, 傳遞的引數是一個URI和一個基於字串的content type.

// create main document part (document.xml) ...
Uri uri = new Uri("/word/document.xml", UriKind.Relative);
string partContentType;
partContentType = "application/vnd.openxmlformats" +
                  "-officedocument.wordprocessingml.document.main+xml";
PackagePart part = package.CreatePart(uri, partContentType);

// get stream for document.xml
StreamWriter streamPart;
streamPart = new StreamWriter(part.GetStream(FileMode.Create,
                                             FileAccess.Write));

對於CreatePart方法的呼叫, 我們傳遞了一個URI, 這個URI基於路徑/word/document.xml, 還傳遞了一個content type, 這個傳遞的content type是Office Open XML檔案格式裡包含主要內容的文件處理部分所需要的. 一旦你建立了一個part, 你就必須序列化你的內容到part中, 方式是通過標準的基於流的程式設計技術來完成序列化的工作. 前面的程式碼通過呼叫GetStream方法打開了一個part上的流, 並且使用這個劉來初始化了一個StreamWriter物件.

The StreamWriter object is used to serialize the “hello world” XML document into document.xml. However, it’s important that you understand what the resulting XML is going to look like. Examine the following XML that represents the simplest of XML documents that can be serialized into document.xml.

StreamWriter物件被用來序列化"Hello world" XML文件到document.xml中 不管怎樣, 你對於結果XML看起來什麼樣子的瞭解還是很重要的. 檢視一下接下來的XML吧, 它代表了可以被序列化到document.xml的最簡單的XML文件.

<?xml version="1.0" encoding="utf-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:body>
    <w:p>
      <w:r>
        <w:t>Hello Open XML</w:t>
      </w:r>
    </w:p>
  </w:body>
</w:document>

注意, 這個XML文件中所有的元素都是被定義在http://schemas.openxmlformats.org/wordprocessingml/2006/main名稱空間下的, 這也是Office Open XML文件格式所要求的. 這個XML文件包含了高層次的文件元素, 文件元素內部是一個body元素, 這個body元素包含了Word文件本身的主要內容(main story).

在body元素之中, 針對每一個段落都有一個<p>元素. 在<p>元素裡是一個定義run的<r>元素. run是一個元素的region, 這個region分享相同的特性集. 在run之中, 是一個<t>元素, 它定義了一個範圍的文字.

It is now time to generate this XML document with code by using the XmlWriter class from the System.Xml namespace. Examine how the following code creates these elements within the proper structure and by using the appropriate namespace.

現在是通過使用System.Xml名稱空間中的XmlWriter類來生成這個XML文件的時候了. 看看下面的程式碼吧, 它在合適的結構下建立了這些元素, 並且使用了合適的名稱空間.

// define string variable for Open XML namespace for nsWP:
string nsWP = "http://schemas.openxmlformats.org" +
               "/wordprocessingml/2006/main";

// write elements into XML document...
XmlWriter writer = XmlWriter.Create(streamPart);
writer.WriteStartDocument();
writer.WriteStartElement("w", "document", nsWP);
writer.WriteStartElement("body", nsWP);
writer.WriteStartElement("p", nsWP);
writer.WriteStartElement("r", nsWP);
writer.WriteStartElement("t", nsWP);
// write hello world text into Word Text element
writer.WriteValue("Hello Open XML");
// close all elements
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
// close XmlWriter object
writer.Close();

我們完成了寫XML內容到document.xml的過程. 最後的一步是建立包(package)與document.xml之間的relationship了. 我們可以通過呼叫Package物件的CreateRelationship方法來建立Relationship. 只要你知道relationship型別的正確的字串值, 並且能為建立的relationship起一個獨一無二的名字, 剩下的就是一個簡單的過程了.

// create the relationship part
string relationshipType;
relationshipType = "http://schemas.openxmlformats.org" +
                   "/officeDocument/2006/relationships/officeDocument";
package.CreateRelationship(uri,
                           TargetMode.Internal,
                           relationshipType,
                           "rId1");
package.Flush();

你可以觀察到在呼叫了CreateRelationship之後, 對於Flush方法的呼叫. 這個呼叫強制打包API使用合適的relationship元素去更新package relationship item. 最後呼叫Package物件的Close方法來完成package的序列化, 並且釋放Hello.docx的檔案控制代碼.

到這裡, 你已經看到了在一個控制檯程式中生成一個簡單.docx檔案的所有必要步驟了.

處於完整性和讀者方便的考慮, 完整列出整個程式碼如下, 您可以在一個控制檯程式中實驗一下.

using System;
using System.IO;
using System.IO.Packaging;
using System.Xml;

namespace HelloDocx {
  class Program {
    static void Main() {

      Package package = Package.Open(@"c:\Data\Hello.docx",
                        FileMode.Create,
                        FileAccess.ReadWrite);

      // create main document part (document.xml) ...
      Uri uri = new Uri("/word/document.xml", UriKind.Relative);
      string partContentType;
      partContentType = "application/vnd.openxmlformats" +
                        "-officedocument.wordprocessingml.document.main+xml";
      PackagePart part = package.CreatePart(uri, partContentType);

      // get stream for document.xml
      StreamWriter streamPart;
      streamPart = new StreamWriter(part.GetStream(FileMode.Create,
                                                   FileAccess.Write));


      // define string variable for Open XML namespace for nsWP:
      string nsWP = "http://schemas.openxmlformats.org" +
                     "/wordprocessingml/2006/main";

      // create the start part, set up the nested structure ...
      XmlWriter writer = XmlWriter.Create(streamPart);
      writer.WriteStartDocument();
      writer.WriteStartElement("w", "document", nsWP);
      writer.WriteStartElement("body", nsWP);
      writer.WriteStartElement("p", nsWP);
      writer.WriteStartElement("r", nsWP);      
      writer.WriteStartElement("t", nsWP);

      writer.WriteValue("My First DOCX File");
      
      writer.WriteEndElement();
      writer.WriteEndElement();
      writer.WriteEndElement();
      writer.WriteEndElement();
      writer.WriteEndElement();
      writer.WriteEndDocument();

      writer.Close();
      
      streamPart.Close();
      package.Flush();

      // create the relationship part
      string relationshipType;
      relationshipType = "http://schemas.openxmlformats.org" +
                         "/officeDocument/2006/relationships/officeDocument";
      package.CreateRelationship(uri, TargetMode.Internal, relationshipType, "rId1");
      package.Flush();

      // close package
      package.Close();
    }
  }
}

譯自<inside Microsoft Windows SharePoint Services 3.0>

轉自:http://www.cnblogs.com/awpatp/archive/2009/12/04/1616822.html