在asp.net中填写word文档?

时间:2009-04-13 14:09:52

标签: asp.net

我正在研究需要填写word文档的Asp.Net项目。我的客户提供了一个包含姓氏,名字,出生日期等的单词模板。我在sql数据库中拥有所有这些信息,客户希望应用程序的用户能够从数据库中下载带有填写信息的word文档。

归档此内容的最佳方式是什么?基本上,我需要在word文档中识别那些“可填充点”,在应用程序用户点击下载按钮时填写这些信息。

8 个答案:

答案 0 :(得分:8)

如果您可以使用Office 2007,那么可以使用Open XML API来格式化文档: http://support.microsoft.com/kb/257757。您必须走这条路的原因是您无法在服务器环境中真正使用Word Automation。 (你可以,但要正常工作是一件巨大的痛苦,并且可以轻松破解)。

如果你不能走2007年的路线,我实际上已经取得了相当不错的成功,只需打开一个单词模板作为流,找到并替换令牌并将其提供给用户。实际上,这实际上在我的经验中表现得非常好,实现起来非常简单。

答案 1 :(得分:6)

我不确定某些ASP.Net方面,但我正在研究类似的东西,你可能想要研究使用RTF。您可以在RTF中使用模式替换。例如,您可以在RTF文档中添加{USER_FIRST_NAME}等标记。当用户单击下载按钮时,您的应用程序可以从数据库中获取信息,并将{USER_FIRST_NAME}的每个实例替换为数据库中的数据。我目前正在使用PHP进行此操作,效果很好。 Word会毫无问题地打开RTF,这是我选择此方法的另一个原因。

答案 2 :(得分:2)

我使用了 Aspose.Words for .NET 。这是有点昂贵的一面,但它的工作非常好,API非常直观,可能非常复杂。

如果您想预先设计文档(或允许其他人为您执行此操作),任何人都可以将fields放入文档中。 Aspose可以打开文档,查找并填写字段,并保存新的已填写的副本以供下载。

答案 3 :(得分:2)

Aspose工作正常,但又一次:价格昂贵。

尽可能避免在网络应用中使用Office Automation。它只是不能很好地扩展。

我对此类问题的首选解决方案是xml:具体来说我推荐WordProcessingML。您可以根据架构创建一个Xml文档,在其上放置一个.doc扩展名,MS Word将打开它,就像它在Office XP中的任何版本中都是本机的一样。这支持大多数Word功能,这样您就可以安全地减少在文本流中替换令牌的问题。

请仔细搜索有关此内容的更多信息:Office 2007与基于Xml的新格式之间存在很多混淆。它们不是一回事。

答案 4 :(得分:1)

此代码适用于WordMl文本框和复选框。它是基于索引的,所以只需传入所有文本框的字符串数组和所有复选框的bool数组。

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();
    }

答案 5 :(得分:0)

通常,您将要避免在服务器上执行Office自动化,而Microsoft甚至要stated that it is a bad idea as well。但是,我通常使用的技术是Office Open XML注意到的aquinas。学习这种格式确实需要花费一些时间,但是一旦你做到这一点就非常值得,因为你不必担心Office自动化涉及的一些问题(例如进程挂起)。

前一段时间我回答了一个类似的问题,你可能会发现它很有用,你可以找到here

答案 6 :(得分:0)

如果您需要在DOC文件(而不是DOCX)中执行此操作,那么OpenXML SDK将无法帮助您。

此外,只想添加另外+1关于在服务器上自动化Office应用程序的危险。你会遇到规模问题 - 我保证。

添加对可用于解决问题的第三方工具的其他引用:

http://www.officewriter.com

OfficeWriter允许您使用完整的API或基于模板的方法(就像您的要求一样)控制文档,它基本上允许您在使用少量代码的情况下打开,绑定和保存DOC和DOCX。

答案 7 :(得分:-3)

你能不能使用微软自己的InterOp Framework来利用Word功能

请参阅Here