Я работаю над функцией, которая позволяет мне создать простой текстовый документ из строки. Я использую DocumentFormat.OpenXml Version="2.20.0" для создания документа Word. Я не понимаю, почему я не могу сохранить документ Word в потоке памяти, тогда как я могу сохранить документ Word в файле.
public Task<byte[]> ConvertToWordAsync(string text)
{
if (text.IsNullOrEmpty())
return Task.FromResult(Array.Empty<byte>());
using var memoryStream = new MemoryStream();
using var wordDocument = WordprocessingDocument.Create(memoryStream, WordprocessingDocumentType.Document);
MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();
mainPart.Document = new Document
{
Body = new Body()
};
Body body = mainPart.Document.Body;
Paragraph paragraph = new Paragraph();
Run run = new Run();
Text bodyText = new Text(text);
run.Append(bodyText);
paragraph.Append(run);
body.Append(paragraph);
wordDocument.Save();
return Task.FromResult(memoryStream.ToArray());
}
Когда я вызываю эту функцию, поток памяти всегда пуст. если я изменюсь
using var wordDocument = WordprocessingDocument.Create(memoryStream, WordprocessingDocumentType.Document);
К
using var wordDocument = WordprocessingDocument.Create("C:\\Workspace\\65.docx", WordprocessingDocumentType.Document);
Я могу открыть файл word.
Я не понимаю, почему я не могу сохранить тот же файл слова в поток памяти. Есть ли у вас какие-либо идеи о решении этой проблемы?
Я ожидаю, что блок автоматического использования будет проблемой здесь. Блок использования для wordDocument должен заканчиваться до return Task.FromResult(memoryStream.ToArray());
или, если вам не нравится этот вызов wordDocument.Close();
, до получения массива из потока памяти.
@jdweng вторая половина вашего комментария, как обычно, не имеет смысла, но, похоже, она вообще не относится к этому вопросу? Здесь никто не говорит о HTTP.
@rene Вы совершенно правы, спасибо. Мне нужно было использовать try/finally и вызвать wordDocument.Dispose(). Теперь работает нормально :)
@CodeCaster Я пытался, но это не решило проблему. Спасибо за помощь в любом случае :)
Да, я удалил этот комментарий, перемотка MemoryStream не требуется, когда вы вызываете для него ToArray(), только при передаче его другому коду, который читает его как поток.
тл;др Не позволяйте компилятору угадывать, где заканчивается блок using, когда вы полагаетесь на вызов Dispose.
using var Foo = new Foo(); Foo.Whatever();
по-прежнему генерирует этот код при компиляции (несущественные детали опущены, см.: Каково использование «using» в C#?):
var Foo = new Foo();
try
{
Foo.Whatever();
}
finally
{
Foo.Dispose();
}
Вызов Dispose здесь актуален.
В этом (детали опущены) коде:
public Task<byte[]> ConvertToWordAsync(string text)
{
using var memoryStream = new MemoryStream();
using var wordDocument = WordprocessingDocument.Create(memoryStream, WordprocessingDocumentType.Document);
// details omitted for brevity
wordDocument.Save();
return Task.FromResult(memoryStream.ToArray());
}
компилятор сгенерировал:
public Task<byte[]> ConvertToWordAsync(string text)
{
var memoryStream = new MemoryStream();
try
{
var wordDocument = WordprocessingDocument.Create(memoryStream, WordprocessingDocumentType.Document);
try
{
// details omitted for brevity
wordDocument.Save();
return Task.FromResult(memoryStream.ToArray());
}
finally
{
wordDocument.Dispose();
}
}
finally
{
memoryStream.Dispose();
}
}
Проблема здесь в том, что WordprocessingDocument
необходимо записать в свой Stream
полный Zip-архив с несколькими файлами, чтобы создать действительный файловый контейнер OpenXml. Это произойдет только тогда, когда больше не ожидается вызовов Save()
. Это происходит либо при вызове Close()
, либо при вызове его метода Dispose()
.
До этого поток в лучшем случае неполный.
Из-за того, что компилятор выдал блоки finally с вызовами методов Dispose, memoryStream
даже не был близок к завершению, когда был вызван ToArray()
. Это было, когда ваш метод вернулся, но не осталось кода для захвата этих данных.
Вы говорите, что решили проблему, явно вызвав Dispose. Это работает. Или вернуться к нормальному синтаксису без сюрпризов:
using var memoryStream = new MemoryStream();
using(var wordDocument = WordprocessingDocument.Create(memoryStream, WordprocessingDocumentType.Document))
{
// details omitted for brevity
wordDocument.Save();
} // wordDocument.Dispose() called here
return Task.FromResult(memoryStream.ToArray());
Тело (содержимое) HTTP-сообщения должно соответствовать Content-Type (см.: geeksforgeeks.org/http-headers-content-type/… ). Также вложения могут быть MIME, которые начинаются с новой строки, содержащей два тире (см.: learn.microsoft.com/en-us/previous-versions/office/developer/…). HTTP-сообщение должно быть в формате HTML, если это текст, или использовать GZIP (сжатие), который преобразует двоичный файл в текст, или использовать строку Base64.