打开 XML 带有 MemoryStream 的 WordprocessingDocument 是 0KB
Open XML WordprocessingDocument with MemoryStream is 0KB
我正在尝试学习如何使用 Microsoft 的 Open XML SDK。我按照他们关于如何使用 FileStream
创建 Word 文档的步骤进行操作,并且效果很好。现在我想创建一个 Word 文档,但只在内存中,并等待用户指定是否要保存文件。
This document by Microsoft 说明如何使用 MemoryStream
处理内存中的文档,但是,文档首先从现有文件中加载,然后 "dumped" 进入 MemorySteam
.我想要的是完全在内存中创建一个文档(而不是基于驱动器中的文件)。我认为可以实现的是这段代码:
// This is almost the same as Microsoft's code except I don't
// dump any files into the MemoryStream
using (var mem = new MemoryStream())
{
using (var doc = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
{
doc.AddMainDocumentPart().Document = new Document();
var body = doc.MainDocumentPart.Document.AppendChild(new Body());
var paragraph = body.AppendChild(new Paragraph());
var run = paragraph.AppendChild(new Run());
run.AppendChild(new Text("Hello docx"));
using (var file = new FileStream(destination, FileMode.CreateNew))
{
mem.WriteTo(file);
}
}
}
但结果是一个 0KB 的文件,Word 无法读取该文件。起初我以为是因为 MemoryStream
的大小,所以我给它提供了 1024 的初始大小,但结果是一样的。另一方面,如果我将 MemoryStream
更改为 FileStream
,它会完美运行。
我的问题是我想做的事情是否可行,如果可行,如何实现?我想这一定是可能的,只是不是我的做法。如果不可能,我有什么选择?
您正在将 mem
传递给 WordprocessingDocument.Create()
,这是从(现在为空)MemoryStream
创建文档,但是,我不认为这是关联MemoryStream
作为文档的后备存储。也就是说,mem
只是文档的 input,而不是 output。因此,当您调用 mem.WriteTo(file);
时,mem
仍然是空的(调试器会确认这一点)。
然后,链接文档确实说 "you must supply a resizable memory stream to [Open()
]",这意味着将写入流,因此可能 mem
成为支持存储,但尚未写入任何内容,因为 AutoSave
property(您在 Create()
中为其指定了 true
)尚未有机会生效(强调我的)...
Gets a flag that indicates whether the parts should be saved when disposed.
我看到 WordprocessingDocument
有一个 SaveAs()
method,并用它代替原始代码中的 FileStream
...
using (var mem = new MemoryStream())
using (var doc = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
{
doc.AddMainDocumentPart().Document = new Document();
var body = doc.MainDocumentPart.Document.AppendChild(new Body());
var paragraph = body.AppendChild(new Paragraph());
var run = paragraph.AppendChild(new Run());
run.AppendChild(new Text("Hello docx"));
// Explicitly close the OpenXmlPackage returned by SaveAs() so destination doesn't stay locked
doc.SaveAs(destination).Close();
}
...为我生成预期的文件。有趣的是,在调用 doc.SaveAs()
之后,即使我插入对 doc.Save()
的调用,mem.Length
和 mem.Position
仍然是 0
,这确实表明mem
仅用于初始化。
我要注意的另一件事是示例代码正在调用 Open()
, whereas you are calling Create()
。就这两种方法的不同之处而言,文档非常稀疏,但我建议您尝试使用 Open()
创建文档...
using (MemoryStream mem = new MemoryStream())
using (WordprocessingDocument doc = WordprocessingDocument.Open(mem, true))
{
// ...
}
... 但是 当我这样做时 Open()
抛出异常,大概是因为 mem
没有数据。因此,这些名称似乎有些不言自明,因为 Create()
初始化新文档数据,而 Open()
期望现有数据。我确实发现如果我喂 Create()
一个装满随机垃圾的 MemoryStream
...
using (var mem = new MemoryStream())
{
// Fill mem with garbage
byte[] buffer = new byte[1024];
new Random().NextBytes(buffer);
mem.Write(buffer, 0, buffer.Length);
mem.Position = 0;
using (var doc = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
{
// ...
}
}
...它仍然生成 与上面的第一个代码片段完全相同的文档 XML,这让我想知道为什么 Create()
甚至需要一个完全输入 Stream
。
这里发生了几件事:
首先,与 Microsoft 的示例不同,我将 using
将文件写入磁盘的块代码嵌套在创建和修改文件的块内。 WordprocessingDocument
被保存到流中,直到它被释放或调用 Save()
方法。 WordprocessingDocument
在到达其 using
块的末尾时会自动处理。如果我没有嵌套第三个 using 语句,从而在尝试保存文件之前到达第二个 using
语句的末尾,我会允许将文档写入 MemoryStream
- 而我是将仍然为空的流写入磁盘(因此是 0KB 文件)。
我 假设 调用 Save()
可能有所帮助,但它不受 .Net 核心(我正在使用的)支持。您可以通过检查 CanSave
.
来检查您的系统是否支持 Save()
/// <summary>
/// Gets a value indicating whether saving the package is supported by calling <see cref="Save"/>. Some platforms (such as .NET Core), have limited support for saving.
/// If <c>false</c>, in order to save, the document and/or package needs to be fully closed and disposed and then reopened.
/// </summary>
public static bool CanSave { get; }
所以代码最终与微软的代码几乎相同,除了我事先没有读取任何文件,而是我只是从一个空的 MemoryStream
:
开始
using (var mem = new MemoryStream())
{
using (var doc = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
{
doc.AddMainDocumentPart().Document = new Document();
var body = doc.MainDocumentPart.Document.AppendChild(new Body());
var paragraph = body.AppendChild(new Paragraph());
var run = paragraph.AppendChild(new Run());
run.AppendChild(new Text("Hello docx"));
}
using (var file = new FileStream(destination, FileMode.CreateNew))
{
mem.WriteTo(file);
}
}
此外,您不需要在保存之前重新打开文档,但如果您确实记得使用 Open()
而不是 Create()
,因为 Create()
会清空 MemoryStream
并且您还将以 0KB 文件结尾。
我正在尝试学习如何使用 Microsoft 的 Open XML SDK。我按照他们关于如何使用 FileStream
创建 Word 文档的步骤进行操作,并且效果很好。现在我想创建一个 Word 文档,但只在内存中,并等待用户指定是否要保存文件。
This document by Microsoft 说明如何使用 MemoryStream
处理内存中的文档,但是,文档首先从现有文件中加载,然后 "dumped" 进入 MemorySteam
.我想要的是完全在内存中创建一个文档(而不是基于驱动器中的文件)。我认为可以实现的是这段代码:
// This is almost the same as Microsoft's code except I don't
// dump any files into the MemoryStream
using (var mem = new MemoryStream())
{
using (var doc = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
{
doc.AddMainDocumentPart().Document = new Document();
var body = doc.MainDocumentPart.Document.AppendChild(new Body());
var paragraph = body.AppendChild(new Paragraph());
var run = paragraph.AppendChild(new Run());
run.AppendChild(new Text("Hello docx"));
using (var file = new FileStream(destination, FileMode.CreateNew))
{
mem.WriteTo(file);
}
}
}
但结果是一个 0KB 的文件,Word 无法读取该文件。起初我以为是因为 MemoryStream
的大小,所以我给它提供了 1024 的初始大小,但结果是一样的。另一方面,如果我将 MemoryStream
更改为 FileStream
,它会完美运行。
我的问题是我想做的事情是否可行,如果可行,如何实现?我想这一定是可能的,只是不是我的做法。如果不可能,我有什么选择?
您正在将 mem
传递给 WordprocessingDocument.Create()
,这是从(现在为空)MemoryStream
创建文档,但是,我不认为这是关联MemoryStream
作为文档的后备存储。也就是说,mem
只是文档的 input,而不是 output。因此,当您调用 mem.WriteTo(file);
时,mem
仍然是空的(调试器会确认这一点)。
然后,链接文档确实说 "you must supply a resizable memory stream to [Open()
]",这意味着将写入流,因此可能 mem
成为支持存储,但尚未写入任何内容,因为 AutoSave
property(您在 Create()
中为其指定了 true
)尚未有机会生效(强调我的)...
Gets a flag that indicates whether the parts should be saved when disposed.
我看到 WordprocessingDocument
有一个 SaveAs()
method,并用它代替原始代码中的 FileStream
...
using (var mem = new MemoryStream())
using (var doc = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
{
doc.AddMainDocumentPart().Document = new Document();
var body = doc.MainDocumentPart.Document.AppendChild(new Body());
var paragraph = body.AppendChild(new Paragraph());
var run = paragraph.AppendChild(new Run());
run.AppendChild(new Text("Hello docx"));
// Explicitly close the OpenXmlPackage returned by SaveAs() so destination doesn't stay locked
doc.SaveAs(destination).Close();
}
...为我生成预期的文件。有趣的是,在调用 doc.SaveAs()
之后,即使我插入对 doc.Save()
的调用,mem.Length
和 mem.Position
仍然是 0
,这确实表明mem
仅用于初始化。
我要注意的另一件事是示例代码正在调用 Open()
, whereas you are calling Create()
。就这两种方法的不同之处而言,文档非常稀疏,但我建议您尝试使用 Open()
创建文档...
using (MemoryStream mem = new MemoryStream())
using (WordprocessingDocument doc = WordprocessingDocument.Open(mem, true))
{
// ...
}
... 但是 当我这样做时 Open()
抛出异常,大概是因为 mem
没有数据。因此,这些名称似乎有些不言自明,因为 Create()
初始化新文档数据,而 Open()
期望现有数据。我确实发现如果我喂 Create()
一个装满随机垃圾的 MemoryStream
...
using (var mem = new MemoryStream())
{
// Fill mem with garbage
byte[] buffer = new byte[1024];
new Random().NextBytes(buffer);
mem.Write(buffer, 0, buffer.Length);
mem.Position = 0;
using (var doc = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
{
// ...
}
}
...它仍然生成 与上面的第一个代码片段完全相同的文档 XML,这让我想知道为什么 Create()
甚至需要一个完全输入 Stream
。
这里发生了几件事:
首先,与 Microsoft 的示例不同,我将 using
将文件写入磁盘的块代码嵌套在创建和修改文件的块内。 WordprocessingDocument
被保存到流中,直到它被释放或调用 Save()
方法。 WordprocessingDocument
在到达其 using
块的末尾时会自动处理。如果我没有嵌套第三个 using 语句,从而在尝试保存文件之前到达第二个 using
语句的末尾,我会允许将文档写入 MemoryStream
- 而我是将仍然为空的流写入磁盘(因此是 0KB 文件)。
我 假设 调用 Save()
可能有所帮助,但它不受 .Net 核心(我正在使用的)支持。您可以通过检查 CanSave
.
Save()
/// <summary>
/// Gets a value indicating whether saving the package is supported by calling <see cref="Save"/>. Some platforms (such as .NET Core), have limited support for saving.
/// If <c>false</c>, in order to save, the document and/or package needs to be fully closed and disposed and then reopened.
/// </summary>
public static bool CanSave { get; }
所以代码最终与微软的代码几乎相同,除了我事先没有读取任何文件,而是我只是从一个空的 MemoryStream
:
using (var mem = new MemoryStream())
{
using (var doc = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
{
doc.AddMainDocumentPart().Document = new Document();
var body = doc.MainDocumentPart.Document.AppendChild(new Body());
var paragraph = body.AppendChild(new Paragraph());
var run = paragraph.AppendChild(new Run());
run.AppendChild(new Text("Hello docx"));
}
using (var file = new FileStream(destination, FileMode.CreateNew))
{
mem.WriteTo(file);
}
}
此外,您不需要在保存之前重新打开文档,但如果您确实记得使用 Open()
而不是 Create()
,因为 Create()
会清空 MemoryStream
并且您还将以 0KB 文件结尾。