【问题标题】:Server Generated Word Doc will Validate but not Open服务器生成的 Word Doc 将验证但不打开
【发布时间】:2014-04-23 22:14:50
【问题描述】:

我正在尝试调整 this example 以使用未安装 Office(无互操作)的服务器上的数据库中的数据填充 Word 模板。我希望能够从数据库中提取模板本身并将生成的填充文档存储回数据库中。

我可以上传和下载模板文件本身,下载的模板仍然可以被 Word 读取。但是当我尝试使用数据填充模板并下载生成的文档时,当我尝试打开文档时收到以下错误:

我们很抱歉。我们无法打开 test.docx,因为我们发现了一个问题 它的内容。
详细信息:没有可用的错误详细信息

自定义 XML 文档 item1.xml 似乎格式正确并包含正确的数据。但是,还有一个 item2.xml 包含以下内容:

<?xml version="1.0"?>
<b:Sources xmlns="http://schemas.openxmlformats.org/officeDocument/2006/bibliography"
   xmlns:b="http://schemas.openxmlformats.org/officeDocument/2006/bibliography" Version="6" StyleName="APA"
   SelectedStyle="\apasixtheditionofficeonline.xsl"/>

我能看到的 XML 中唯一可疑的区别是 docProps\app.xml。在模板中有这样的:

<Template>EquipmentOffering.dotx</Template>
<TotalTime>950</TotalTime>
<Pages>5</Pages>
<Words>2470</Words>
<Characters>14084</Characters>
<Application>Microsoft Office Word</Application>
<DocSecurity>0</DocSecurity>
<Lines>117</Lines>
<Paragraphs>33</Paragraphs>

在生成的文档中我有这个:

<Template>EquipmentOffering.dotx</Template>
<TotalTime>1297</TotalTime>
<Pages>1</Pages>
<Words>2486</Words>
<Characters>14176</Characters>
<Application>Microsoft Office Word</Application>
<DocSecurity>0</DocSecurity>
<Lines>118</Lines>
<Paragraphs>33</Paragraphs>

(该文件中显然还有更多内容,但这是可疑的部分)

具体来说,&lt;Pages&gt; 元素让我担心。

这是我对示例代码的实现:

Private Const strRelRoot As String = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"

Protected Overrides Sub Execute()
    'Get the parameters and the template to be filled
    Dim params As CreateOfferingParams = Parameters
    Dim serverWorkSpace = Application.Current.CreateDataWorkspace()
    Dim offeringTemps = From sws In serverWorkSpace.myDatabase_dbData.DocTemplates
                        Where sws.TemplateName.Contains("EquipmentOffering")
                        Select sws

    'Open the template and copy it into a local byte array
    Dim myTemplate As DocTemplate = offeringTemps.FirstOrDefault()
    Dim fileBuffer As Byte() = myTemplate.TemplateFile

    'Open the file stream in memory, create a file package, and extract the relationship collection
    Using pkgStream As MemoryStream = New MemoryStream(fileBuffer, True)
        Using pkgFile As Package = Package.Open(pkgStream, FileMode.Open, FileAccess.ReadWrite)
            Dim pkgrcOfficeDocument As PackageRelationshipCollection = pkgFile.GetRelationshipsByType(strRelRoot)

            'Scan each relationship looking for custom XML
            For Each pkgr As PackageRelationship In pkgrcOfficeDocument
                If (pkgr.SourceUri.OriginalString = "/") Then

                    'Add a custom XML part to the package
                    Dim uriData As Uri = New Uri("/customXML/item1.xml", UriKind.Relative)
                    If pkgFile.PartExists(uriData) Then
                        'Delete template "/customXML/item1.xml" part
                        pkgFile.DeletePart(uriData)
                    End If

                    'Load the custom XML data
                    Dim pkgprtData As PackagePart = pkgFile.CreatePart(uriData, "application/xml")
                    Using docStream As Stream = pkgprtData.GetStream()
                        Using writer As XmlWriter = XmlWriter.Create(docStream)

                            'Write the root element and our namespace
                            writer.WriteStartDocument()
                            writer.WriteStartElement("Offering", "http://www.acrison.com")

                            'Write in each data element
                            For Each element In params.OfferingData
                                writer.WriteElementString(element.Key, element.Value)
                            Next

                            writer.WriteEndElement()
                            writer.WriteEndDocument()
                        End Using
                    End Using
                End If
            Next

            'Rewind the stream
            pkgStream.Position = 0

            'Copy the modified package into a new byte array
            Dim generatedOffering As Byte() = New Byte((pkgStream.Length) - 1) {}
            Dim i As Integer = 0
            Do While (i < pkgStream.Length)
                generatedOffering(i) = CType(pkgStream.ReadByte, Byte)
                i = (i + 1)
            Loop

            'Get the appropriate Quote entity so we have somewhere to store the newly generated file
            Dim quoteInfo = From sws In serverWorkSpace.myDatabase_dbData.Quotes
                            Where sws.QuoteID = params.QuoteIDParam
                            Select sws

            Dim myQuote As Quote = quoteInfo.FirstOrDefault()

            'Store the file in the entity
            myQuote.QuoteOffering = generatedOffering

            'Save out the changes
            serverWorkSpace.myDatabase_dbData.SaveChanges()

        End Using
    End Using
End Sub

有人看到我做错了什么会导致.docx 文件损坏吗?


根据 cmets 的要求,我创建了一个简单的三控件模板并尝试生成一个看起来损坏的文件。 Dropbox 链接到模板文件并生成 Word .docx

我在这样做时注意到了一件奇怪的事情。尝试在 Word 2013 中打开 .docx 会导致上述错误。但是 Dropbox 可以成功预览它,尽管内容控件看起来就像没有数据绑定到它们一样。我不确定这意味着什么。


在阅读了@Flowerking 提供的链接并尝试了许多示例解决方案后,我遇到了this SO Answer,它使用了 Open XML 2.0,并且似乎适用于 OP。结合我所拥有的,我在链接博客上读到的一些内容,以及那个答案,我得出了以下结论:

Protected Overrides Sub Execute()
    'Get the parameters and the template to be filled
    Dim params As CreateOfferingParams = Parameters
    Dim serverWorkSpace = Application.Current.CreateDataWorkspace()
    Dim offeringTemps = From sws In serverWorkSpace.aris_dbData.DocTemplates
                        Where sws.TemplateName.Contains("SimpleTest")
                        Select sws

    'Open the template and copy it into a local byte array
    Dim myTemplate As DocTemplate = offeringTemps.FirstOrDefault()
    Dim fileBuffer As Byte() = myTemplate.TemplateFile

    'Open the file stream in memory and create a document object from it
    'Must be done this way so stream is expandable
    Dim pkgStream As MemoryStream = New MemoryStream
    pkgStream.Write(fileBuffer, 0, fileBuffer.Length)

    Using docTemplate As WordprocessingDocument = WordprocessingDocument.Open(pkgStream, True)
        'Create XML string matching custom XML part
        'Extract the main part of the document and replace any custom XML
        Dim mainPart As MainDocumentPart = docTemplate.MainDocumentPart

        For Each part As CustomXmlPart In mainPart.CustomXmlParts
            Dim docStream As New MemoryStream
            Dim docWriter As XmlWriter = XmlTextWriter.Create(docStream)

            'Write the declaration, root element and our namespace
            docWriter.WriteStartDocument()
            docWriter.WriteStartElement("Offering", "http://www.mycompany.com")

            'Write in each data element
            For Each element In params.OfferingData
                docWriter.WriteElementString(element.Key, element.Value)
            Next

            'Close each of the tags and flush the stream
            docWriter.WriteEndElement()
            docWriter.WriteEndDocument()
            docWriter.Flush()

            'Rewind the stream and feed it to the custom XML part we are working on
            docStream.Seek(0, SeekOrigin.Begin)
            part.FeedData(docStream)

            'Rewind the package stream
            pkgStream.Position = 0

            'Copy the modified package into a new byte array
            Dim generatedOffering As Byte() = New Byte((pkgStream.Length) - 1) {}
            Dim i As Integer = 0
            Do While (i < pkgStream.Length)
                generatedOffering(i) = CType(pkgStream.ReadByte, Byte)
                i = (i + 1)
            Loop

            'Get the appropriate Quote entity so we have somewhere to store the newly generated file
            Dim quoteInfo = From sws In serverWorkSpace.aris_dbData.Quotes
                            Where sws.QuoteID = params.QuoteIDParam
                            Select sws

            Dim myQuote As Quote = quoteInfo.FirstOrDefault()

            'Store the file in the entity
            myQuote.QuoteOffering = generatedOffering

            'Save out the changes
            serverWorkSpace.aris_dbData.SaveChanges()
        Next
    End Using
End Sub

我相信我现在只使用正确的 Open XML 2.5 方法,但结果是一样的,我无法打开生成的 .docx。我什至使用过 Open XML SDK 2.5 Productivity Tool,生成的 .docx 在 Word 2007、2010 和 2013 下验证。

Dropbox 中的文档已更新为我目前正在使用的文档。有人能看出他们有什么问题吗?

【问题讨论】:

  • 不检查就不确定,但 .docx 等中的 itemn.xml 文件通常具有关联的 itemPropsn.xml 文件和一个与另一个的关系。引用的示例是旧的 (2006) - 可能是为不需要的 word 2007 的预发布版本编写的?
  • @bibadia 道具文件确实存在。经过仔细检查,我注意到它引用了错误的架构。但是,使用正确架构上传模板的最新副本并不能解决问题。
  • 我不会担心 pages 元素。如果您将 docx 放在人们可能会为您诊断的地方,您将增加解决此问题的几率。或者您可以使用 Open XML Power Tools 将模板与生成的 docx 进行比较。
  • @JasonPlutext 查看我的编辑。我已经上传了文件并注意到一些奇怪的行为。即将下载 Open XML Power Tools 并试一试。

标签: .net vb.net ms-word openxml


【解决方案1】:

我认为您的代码对 Word 2007 中支持自定义 XMl 部分的旧 Open XML 有效。但是 Microsoft 丢失了 patent case 并且必须从 word open xml 中删除自定义 xml。

Dropbox 正在正确显示文档,因为它将其视为 word open xml 2007 格式。您只需将文档的扩展名从 .docx 重命名为 .doc 即可,word 可以打开它而不会出错,但内容控件中缺少数据。

最好的解决方案是遵循最新的开放 xml 规范,而不是使用自定义 xml 部分。查看this blog 中的一些使用内容控件的示例。

希望这会有所帮助。

【讨论】:

  • 您的链接很有帮助,但我仍然遇到同样的错误。请查看更新后的问题和链接文件。如果您发现我在 2007 年后 Word Open XML 方面做错了什么,请告诉我。
【解决方案2】:

主要问题是我的模板是.dotx,而我将生成的文件保存为.docx。将生成的文件的扩展名更改为.dotx我能够打开它并查看内容。

我不确定 SDK 中是否有进行转换的方法。但只需将初始模板保存为 .docx 即可解决问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-16
    • 1970-01-01
    相关资源
    最近更新 更多