服务器生成的Word文档将验证但不会打开

时间:2014-04-03 18:11:08

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

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

我可以上传和下载模板文件本身,下载的模板仍可由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>

(该文件显然更多,但这是可疑部分)

具体来说,<Pages>元素让我担心。

以下是我对示例代码的实现:

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文件?


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

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


在阅读了@Flowerking提供的链接并尝试了许多示例解决方案之后,我遇到了this所以使用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。我甚至在Word 2007,2010和2013下使用了Open XML SDK 2.5 Productivity Tool和生成的.docx验证。

Dropbox中的文档已更新为我目前正在使用的文档。任何人都可以看到他们有什么问题吗?

2 个答案:

答案 0 :(得分:1)

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

Dropbox正在正确显示该文档,因为它将其视为单词open xml 2007格式。您可以简单地将文档的扩展名从.docx重命名为.doc,然后word可以无错误地打开它,但内容控件中的数据丢失。

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

希望这会有所帮助。

答案 1 :(得分:0)

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

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