2009-04-13 56 views
6

我正在開發需要填寫word文檔的Asp.Net項目。我的客戶提供帶姓氏,名字,出生日期等的單詞模板。我在sql數據庫中擁有所有這些信息,並且客戶端希望應用程序的用戶能夠使用數據庫中的填充信息下載Word文檔。在asp.net中填寫word文檔?

什麼是最好的方法來歸檔這個?基本上,我需要識別word文檔中的「可填寫的地方」,填寫應用程序用戶單擊下載按鈕時的信息。

回答

6

我不確定一些ASP.Net方面,但我正在做類似的工作,你可能想用RTF來代替。您可以在RTF中使用模式替換。例如,您可以在RTF文檔中添加{USER_FIRST_NAME}之類的標籤。當用戶點擊下載按鈕時,您的應用程序可以從數據庫中獲取信息,並將{USER_FIRST_NAME}的每個實例替換爲數據庫中的數據。我目前正在用PHP來做這件事,而且效果很好。 Word會打開RTF而不會出現問題,這是我選擇此方法的另一個原因。

2

我已經使用Aspose.Words for .NET。它的價格有點貴,但它工作得非常好,對於可能非常複雜的東西,API非常直觀。

如果您想要預先設計文檔(或允許其他人爲您做),任何人都可以將fields放入文檔中。 Aspose可以打開文檔,查找和填充字段,並保存一份新的已填寫副本以供下載。

2

Aspose的工作好,但再次:它是昂貴的。

儘可能避免在Web應用程序中使用Office Automation。它只是不能很好地擴展。

我對這類問題的首選解決方案是xml:具體在這​​裏我建議WordProcessingML。您可以根據模式創建一個Xml文檔,並在其上放置一個.doc擴展名,MS Word將打開它,就好像它在任何版本中都是本機的,就像Office XP一樣。這支持大多數Word功能,這樣您就可以安全地將問題減少到替換文本流中的令牌。

請仔細閱讀Google搜索以瞭解更多相關信息:Office 2007中基於Xml格式和新版本之間存在很多混淆。它們不是一回事。

8

如果您可以使用Office 2007,則需要使用Open XML API格式化文檔: http://support.microsoft.com/kb/257757。您必須走這條路線的原因是您無法真正在服務器環境中使用Word Automation。 (你可以,但是正確工作是一件非常痛苦的事情,而且容易中斷)。

如果你不能去2007年的路線,我實際上已經取得了相當不錯的成功,只是打開一個字模板作爲一個流,找到並替換令牌並將其提供給用戶。這實際上在我的經驗中表現出色,並且實現起來非常簡單。

+0

先前版本的Word可以打開這些文檔;你只需要從MS下載更新。 – Will 2009-04-13 14:22:41

+0

我的答案中較舊的xml樣式將無需任何更新即可工作。 – 2009-04-13 14:24:42

-3

難道你不使用微軟自己的互操作框架利用的Word功能

Here

+1

這是一個皇家的痛苦,讓這個工作,並保持它在服務器環境中工作。不建議。 – Knobloch 2009-04-13 14:39:20

1

此代碼爲WordML中的文本框和複選框。它是基於索引的,所以只需傳入所有文本框的字符串數組和所有複選框的布爾數組。

public void FillInFields(
    Stream sourceStream, 
    Stream destinationStream, 
    bool[] pageCheckboxFields, 
    string[] pageTextFields 
    ) { 

    StreamUtil.Copy(sourceStream, destinationStream); 
    sourceStream.Close(); 

    destinationStream.Seek(0, SeekOrigin.Begin); 

    Package package = Package.Open(destinationStream, FileMode.Open, FileAccess.ReadWrite); 
    Uri uri = new Uri("/word/document.xml", UriKind.Relative); 

    PackagePart packagePart = package.GetPart(uri); 
    Stream documentPart = packagePart.GetStream(FileMode.Open, FileAccess.ReadWrite); 
    XmlReader xmlReader = XmlReader.Create(documentPart); 

    XDocument xdocument = XDocument.Load(xmlReader); 

    List<XElement> textBookmarksList = xdocument 
     .Descendants(w + "fldChar") 
     .Where(e => (e.AttributeOrDefault(w + "fldCharType") ?? "") == "separate") 
     .ToList(); 

    var textBookmarks = textBookmarksList.Select(e => new WordMlTextField(w, e, textBookmarksList.IndexOf(e))); 

    List<XElement> checkboxBookmarksList = xdocument 
     .Descendants(w + "checkBox") 
     .ToList(); 

    IEnumerable<WordMlCheckboxField> checkboxBookmarks = checkboxBookmarksList 
     .Select(e => new WordMlCheckboxField(w, e, checkboxBookmarksList.IndexOf(e))); 

    for (int i = 0; i < pageTextFields.Length; i++) { 
     string value = pageTextFields[i]; 
     if (!String.IsNullOrEmpty(value)) 
      SetWordMlElement(textBookmarks, i, value); 
    } 

    for (int i = 0; i < pageCheckboxFields.Length; i++) { 
     bool value = pageCheckboxFields[i]; 
     SetWordMlElement(checkboxBookmarks, i, value); 
    } 

    PackagePart newPart = packagePart; 
    StreamWriter streamWriter = new StreamWriter(newPart.GetStream(FileMode.Create, FileAccess.Write)); 
    XmlWriter xmlWriter = XmlWriter.Create(streamWriter); 
    if (xmlWriter == null) throw new Exception("Could not open an XmlWriter to 4311Blank-1.docx."); 
    xdocument.Save(xmlWriter); 

    xmlWriter.Close(); 
    streamWriter.Close(); 
    package.Flush(); 

    destinationStream.Seek(0, SeekOrigin.Begin); 
} 

private class WordMlTextField { 
    public int? Index { get; set; } 
    public XElement TextElement { get; set; } 

    public WordMlTextField(XNamespace ns, XObject element, int index) { 
     Index = index; 

     XElement parent = element.Parent; 
     if (parent == null) throw new NicException("fldChar must have a parent."); 
     if (parent.Name != ns + "r") { 
      log.Warn("Expected parent of fldChar to be a run for fldChar at position '" + Index + "'"); 
      return; 
     } 
     var nextSibling = parent.ElementsAfterSelf().First(); 

     if (nextSibling.Name != ns + "r") { 
      log.Warn("Expected a 'r' element after the parent of fldChar at position = " + Index); 
      return; 
     } 

     var text = nextSibling.Element(ns + "t"); 
     if (text == null) { 
      log.Warn("Expected a 't' element inside the 'r' element after the parent of fldChar at position = " + Index); 
     } 

     TextElement = text; 
    } 
} 

private class WordMlCheckboxField { 
    public int? Index { get; set; } 
    public XElement CheckedElement { get; set; } 
    public readonly XNamespace _ns; 

    public WordMlCheckboxField(XNamespace ns, XContainer checkBoxElement, int index) { 
     _ns = ns; 
     Index = index; 

     XElement checkedElement = checkBoxElement.Elements(ns + "checked").FirstOrDefault(); 
     if (checkedElement == null) { 
      checkedElement = new XElement(ns + "checked", new XAttribute(ns + "val", "0")); 
      checkBoxElement.Add(checkedElement); 
     } 

     CheckedElement = checkedElement; 
    } 

    public static void Copy(Stream readStream, Stream writeStream) { 
     const int Length = 256; 
     Byte[] buffer = new Byte[Length]; 
     int bytesRead = readStream.Read(buffer, 0, Length); 
     // write the required bytes 
     while (bytesRead > 0) { 
      writeStream.Write(buffer, 0, bytesRead); 
      bytesRead = readStream.Read(buffer, 0, Length); 
     } 
     readStream.Flush(); 
     writeStream.Flush(); 
    } 
+0

也許你可以在樣本中添加一些上下文? – 2009-04-13 14:40:32

0

一般而言,您將希望避免在服務器上執行Office自動化,而且Microsoft甚至可以使用stated that it is a bad idea as well。然而,我通常使用的技術是aquinas所指出的Office Open XML。在格式上花費一些時間來學習,但是一旦完成,它就值得一試,因爲您不必擔心與Office自動化有關的一些問題(例如流程掛起)。

一段時間回來,我回答了一個類似的問題,你可能會發現它有用,你可以找到它here

0

如果您需要在DOC文件(而不是DOCX)中執行此操作,那麼OpenXML SDK不會對您有所幫助。

此外,只是想添加另一個+1關於在服務器上自動執行Office應用程序的危險。你會遇到規模問題 - 我保證。

要另添加引用第三方的工具,可以用來解決您的問題:

http://www.officewriter.com

OfficeWriter讓你控制一個完整的API,或基於模板的方法文檔(如你的要求是什麼),基本上可以讓你打開,綁定,並保存DOC和DOCX在這樣的情況下用很少的代碼。