【发布时间】: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>
(该文件中显然还有更多内容,但这是可疑的部分)
具体来说,<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 文件损坏吗?
根据 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