我正在開發需要填寫word文檔的Asp.Net項目。我的客戶提供帶姓氏,名字,出生日期等的單詞模板。我在sql數據庫中擁有所有這些信息,並且客戶端希望應用程序的用戶能夠使用數據庫中的填充信息下載Word文檔。在asp.net中填寫word文檔?
什麼是最好的方法來歸檔這個?基本上,我需要識別word文檔中的「可填寫的地方」,填寫應用程序用戶單擊下載按鈕時的信息。
我正在開發需要填寫word文檔的Asp.Net項目。我的客戶提供帶姓氏,名字,出生日期等的單詞模板。我在sql數據庫中擁有所有這些信息,並且客戶端希望應用程序的用戶能夠使用數據庫中的填充信息下載Word文檔。在asp.net中填寫word文檔?
什麼是最好的方法來歸檔這個?基本上,我需要識別word文檔中的「可填寫的地方」,填寫應用程序用戶單擊下載按鈕時的信息。
我不確定一些ASP.Net方面,但我正在做類似的工作,你可能想用RTF來代替。您可以在RTF中使用模式替換。例如,您可以在RTF文檔中添加{USER_FIRST_NAME}之類的標籤。當用戶點擊下載按鈕時,您的應用程序可以從數據庫中獲取信息,並將{USER_FIRST_NAME}的每個實例替換爲數據庫中的數據。我目前正在用PHP來做這件事,而且效果很好。 Word會打開RTF而不會出現問題,這是我選擇此方法的另一個原因。
我已經使用Aspose.Words for .NET。它的價格有點貴,但它工作得非常好,對於可能非常複雜的東西,API非常直觀。
如果您想要預先設計文檔(或允許其他人爲您做),任何人都可以將fields放入文檔中。 Aspose可以打開文檔,查找和填充字段,並保存一份新的已填寫副本以供下載。
Aspose的工作好,但再次:它是昂貴的。
儘可能避免在Web應用程序中使用Office Automation。它只是不能很好地擴展。
我對這類問題的首選解決方案是xml:具體在這裏我建議WordProcessingML。您可以根據模式創建一個Xml文檔,並在其上放置一個.doc
擴展名,MS Word將打開它,就好像它在任何版本中都是本機的,就像Office XP一樣。這支持大多數Word功能,這樣您就可以安全地將問題減少到替換文本流中的令牌。
請仔細閱讀Google搜索以瞭解更多相關信息:Office 2007中基於Xml格式和新版本之間存在很多混淆。它們不是一回事。
如果您可以使用Office 2007,則需要使用Open XML API格式化文檔: http://support.microsoft.com/kb/257757。您必須走這條路線的原因是您無法真正在服務器環境中使用Word Automation。 (你可以,但是正確工作是一件非常痛苦的事情,而且容易中斷)。
如果你不能去2007年的路線,我實際上已經取得了相當不錯的成功,只是打開一個字模板作爲一個流,找到並替換令牌並將其提供給用戶。這實際上在我的經驗中表現出色,並且實現起來非常簡單。
此代碼爲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();
}
也許你可以在樣本中添加一些上下文? – 2009-04-13 14:40:32
一般而言,您將希望避免在服務器上執行Office自動化,而且Microsoft甚至可以使用stated that it is a bad idea as well。然而,我通常使用的技術是aquinas所指出的Office Open XML。在格式上花費一些時間來學習,但是一旦完成,它就值得一試,因爲您不必擔心與Office自動化有關的一些問題(例如流程掛起)。
一段時間回來,我回答了一個類似的問題,你可能會發現它有用,你可以找到它here。
如果您需要在DOC文件(而不是DOCX)中執行此操作,那麼OpenXML SDK不會對您有所幫助。
此外,只是想添加另一個+1關於在服務器上自動執行Office應用程序的危險。你會遇到規模問題 - 我保證。
要另添加引用第三方的工具,可以用來解決您的問題:
OfficeWriter讓你控制一個完整的API,或基於模板的方法文檔(如你的要求是什麼),基本上可以讓你打開,綁定,並保存DOC和DOCX在這樣的情況下用很少的代碼。
先前版本的Word可以打開這些文檔;你只需要從MS下載更新。 – Will 2009-04-13 14:22:41
我的答案中較舊的xml樣式將無需任何更新即可工作。 – 2009-04-13 14:24:42