首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

使用 OpenXML 替換 Word 模板中的書籤標籤

     編輯:關於.NET

前言

在近期的 MOSS 專案中,需求是根據 InfoPath 表單內容與 Word 模板生成對應的 Word 文件。若使用 Word COM API,會面臨後台進程管理、多用戶並發等棘手問題。因此選擇使用 OpenXML SDK 進行純伺服器端處理,避免安裝 Office 且效能更佳。

核心實作步驟

1. 環境準備

首先需引用 WindowsBase.dll,並定義必要的常數:

const string documentRelationshipType = "https://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
const string strUri = "https://schemas.openxmlformats.org/wordprocessingml/2006/main";
static List<string> bookmarkText;
string bookmarkName;

2. 解析模板中的書籤

以下方法會遍歷文件中的所有書籤起始標籤(w:bookmarkStart),提取書籤名稱與現有內容,存入 DataTable 供後續繫結:

void bindTable(string fileName) {
    dt = new DataTable();
    dt.Columns.Add("標籤", typeof(string));
    dt.Columns.Add("內容", typeof(string));
    DataRow dr;
    Stream strm = null;
    PackagePart documentPart;
    Package package;

    using (SPSite site = new SPSite("https://ascentn-moss:8686/")) {
        SPWeb web = site.OpenWeb();
        strm = web.GetFile(fileName).OpenBinaryStream();
        package = Package.Open(strm, FileMode.Open, FileAccess.ReadWrite);

        foreach (PackageRelationship documentRelationship in package.GetRelationshipsByType(documentRelationshipType)) {
            NameTable nt = new NameTable();
            XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
            nsManager.AddNamespace("w", strUri);
            Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), documentRelationship.TargetUri);
            documentPart = package.GetPart(documentUri);
            XmlDocument xdoc = new XmlDocument();
            xdoc.Load(documentPart.GetStream());
            XmlNodeList nodeList = xdoc.SelectNodes("//w:bookmarkStart", nsManager);

            foreach (XmlNode node in nodeList) {
                dr = dt.NewRow();
                if (node.NextSibling.Name == "w:bookmarkEnd") {
                    // 空書籤
                    bookmarkText = new List<string>();
                    bookmarkName = node.Attributes["w:name"].Value;
                    bookmarkText.Add(" ");
                    dr[0] = bookmarkName;
                } else {
                    // 含內容書籤
                    bookmarkName = node.Attributes["w:name"].Value;
                    dr[0] = bookmarkName;
                    string bookmarkId = node.Attributes["w:id"].Value;
                    bookmarkText = new List<string>();
                    XmlNode nextParentNode = node.ParentNode;
                    XmlNode nodeIterate = node.NextSibling;
                    while (nodeIterate.Name != "w:bookmarkEnd") {
                        if (nodeIterate.Name == "w:r")
                            bookmarkText.Add(nodeIterate.InnerText);
                        nodeIterate = nodeIterate.NextSibling;
                        if (nodeIterate == null) {
                            // 跨段落處理
                            if (nextParentNode.NextSibling.Attributes.Count != 0) {
                                if (nextParentNode.NextSibling.Attributes["w:id"].Value == bookmarkId) {
                                    nodeIterate = nextParentNode.NextSibling;
                                }
                            } else {
                                nextParentNode = nextParentNode.NextSibling;
                                nodeIterate = nextParentNode.FirstChild;
                                bookmarkText.Add('\n' + nodeIterate.InnerText);
                            }
                        }
                    }
                }
                dt.Rows.Add(dr);
            }
        }
        package.Close();
    }
}

3. 以 InfoPath 資料替換書籤內容

取得書籤清單後,透過 Web Service 複製模板檔案,再以 OpenXML 逐一替換書籤內容,最後移除書籤標籤本身:

#region 處理書籤
string spUrl = (string)ds["SPServer"];
APWS.Service apws = new APWS.Service();
apws.Url = spUrl + "/spsws/Service.asmx";
apws.Credentials = WFSystem.GetSystemUserCredential();

System.IO.MemoryStream fileStream = new System.IO.MemoryStream();
string newfilename = fileLocation.EndsWith("/") 
    ? fileLocation + fileName + template.Substring(template.LastIndexOf('.'))
    : fileLocation + "/" + fileName + template.Substring(template.LastIndexOf('.'));

apws.SPCopyFile(template, newfilename);
fileStream.Write(apws.SPOpenBinary(newfilename), 0, apws.SPGetFileLenght(newfilename));

originalDocPackage = Package.Open(fileStream, FileMode.Open, FileAccess.ReadWrite);
foreach (PackageRelationship relationship in originalDocPackage.GetRelationshipsByType(documentRelationshipType)) {
    Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);
    originalDocumentPart = originalDocPackage.GetPart(documentUri);
    break;
}

originalXmlDocument = new XmlDocument();
originalXmlDocument.Load(originalDocumentPart.GetStream());
NameTable nt = new NameTable();
XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(nt);
xmlNamespaceManager.AddNamespace("w", strUri);

#region 多書籤迴圈檢索
XmlNodeList bookmarknodeList = originalXmlDocument.SelectNodes("//w:bookmarkStart", xmlNamespaceManager);
XmlNodeList bookmarknodeEndList = originalXmlDocument.SelectNodes("//w:bookmarkEnd", xmlNamespaceManager);
XmlNode parentNodeOfBookMark;

foreach (XmlNode xmlnode in bookmarknodeList) {
    foreach (string columnName in hvalues.Keys) {
        if (xmlnode.Attributes["w:name"].Value == columnName) {
            if (xmlnode.NextSibling.Name == "w:r") {
                xmlnode.NextSibling.RemoveChild(xmlnode.NextSibling.LastChild);
                XmlElement textElement = originalXmlDocument.CreateElement("w:t", strUri);
                xmlnode.NextSibling.AppendChild(textElement);
                XmlNode textNode = originalXmlDocument.CreateNode(XmlNodeType.Text, "w:t", strUri);
                // 日期格式處理
                if (hvalues[columnName].ToString().EndsWith("T00:00:00"))
                    textNode.Value = hvalues[columnName].ToString().Substring(0, hvalues[columnName].ToString().IndexOf('T'));
                else
                    textNode.Value = hvalues[columnName].ToString();
                textElement.AppendChild(textNode);
            }
        }
    }
    parentNodeOfBookMark = xmlnode.ParentNode;
    parentNodeOfBookMark.RemoveChild(xmlnode);
}

foreach (XmlNode xmlnode in bookmarknodeEndList) {
    parentNodeOfBookMark = xmlnode.ParentNode;
    parentNodeOfBookMark.RemoveChild(xmlnode);
}
#endregion

// 儲存文件
originalXmlDocument.Save(originalDocumentPart.GetStream(FileMode.Create, FileAccess.Write));
byte[] bs = fileStream.ToArray();
apws.SPSaveBinary(newfilename, bs);
originalDocPackage.Close();
#endregion

總結

此方案完全在伺服器端運行,無需安裝 Office,適合高併發環境。完整原始碼可參考:https://www.bianceng.net/dotnet/201212/808.htm

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved