На самом деле есть только 2 способа открыть документ Word программно - в виде физического файла или в виде потока. Есть «пакет», но это не совсем применимо.
Метод потока описан здесь: https://docs.microsoft.com/en-us/office/open-xml/how-to-open-a-word-processing-document-from-a-stream
Но даже он зависит от наличия физического файла для формирования потока:
string strDoc = @"C:\Users\Public\Public Documents\Word13.docx";
Stream stream = File.Open(strDoc, FileMode.Open);
Лучшее решение, которое я могу предложить, - записать файл во временную папку, в которой учетная запись службы для приложения имеет разрешение на запись:
string newDocument = @"C:\temp\test.docx";
WriteFile(byteArray, newDocument);
Если бы у меня не было разрешений для папки «temp» в моем примере, вы просто добавили бы учетную запись службы вашего приложения (пул приложений, если это веб-сайт), чтобы иметь полный контроль над папкой.
Вы бы использовали эту WriteFile()
функцию:
/// <summary>
/// Write a byte[] to a new file at the location where you choose
/// </summary>
/// <param name="byteArray">byte[] that consists of file data</param>
/// <param name="newDocument">Path to where the new document will be written</param>
public static void WriteFile(byte[] byteArray, string newDocument)
{
using (MemoryStream stream = new MemoryStream())
{
stream.Write(byteArray, 0, (int)byteArray.Length);
// Save the file with the new name
File.WriteAllBytes(newDocument, stream.ToArray());
}
}
Оттуда вы можете открыть его с помощью OpenXML и отредактировать файл. Невозможно открыть документ Word в форме byte [] непосредственно в экземпляре Word - Interop, OpenXML или иным образом - потому что вам нужен documentPath
или упомянутый ранее потоковый метод, основанный на наличии физического файла. Вы можете отредактировать байты, которые вы получите, прочитав байты в строку, а затем XML или просто отредактировав строку:
string docText = null;
byte[] byteArray = null;
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(documentPath, true))
{
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd(); // <-- converts byte[] stream to string
}
// Play with the XML
XmlDocument xml = new XmlDocument();
xml.LoadXml(docText); // the string contains the XML of the Word document
XmlNodeList nodes = xml.GetElementsByTagName("w:body");
XmlNode chiefBodyNode = nodes[0];
// add paragraphs with AppendChild...
// remove a node by getting a ChildNode and removing it, like this...
XmlNode firstParagraph = chiefBodyNode.ChildNodes[2];
chiefBodyNode.RemoveChild(firstParagraph);
// Or play with the string form
docText = docText.Replace("John","Joe");
// If you manipulated the XML, write it back to the string
//docText = xml.OuterXml; // comment out the line above if XML edits are all you want to do, and uncomment out this line
// Save the file - yes, back to the file system - required
using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
{
sw.Write(docText);
}
}
// Read it back in as bytes
byteArray = File.ReadAllBytes(documentPath); // new bytes, ready for DB saving
Справка:
https://docs.microsoft.com/en-us/office/open-xml/how-to-search-and-replace-text-in-a-document-part
Я знаю, что это не идеально, но я искал и не нашел способа отредактировать byte[]
напрямую без преобразования, которое включает в себя выписывание файла, открытие его в Word для редактирования, а затем, по существу, повторную загрузку его в восстановить новые байты. Выполнение byte[] byteArray = Encoding.UTF8.GetBytes(docText);
перед повторным чтением файла повредит их, как и любой другой Encoding
, который я пробовал (UTF7
, Default
, Unicode
, ASCII
), как я обнаружил, когда пытался писать они возвращаются, используя мою WriteFile()
функцию выше, в последней строке. Когда кодировался и просто собирался с использованием File.ReadAllBytes()
, а затем записывал байты обратно с использованием WriteFile()
, он работал нормально.
Обновление:
Возможно, можно манипулировать байтами следующим образом:
//byte[] byteArray = File.ReadAllBytes("Test.docx"); // you might be able to assign your bytes here, instead of from a file?
byte[] byteArray = GetByteArrayFromDatabase(fileId); // function you have for getting the document from the database
using (MemoryStream mem = new MemoryStream())
{
mem.Write(byteArray, 0, (int)byteArray.Length);
using (WordprocessingDocument wordDoc =
WordprocessingDocument.Open(mem, true))
{
// do your updates -- see string or XML edits, above
// Once done, you may need to save the changes....
//wordDoc.MainDocumentPart.Document.Save();
}
// But you will still need to save it to the file system here....
// You would update "documentPath" to a new name first...
string documentPath = @"C:\temp\newDoc.docx";
using (FileStream fileStream = new FileStream(documentPath,
System.IO.FileMode.CreateNew))
{
mem.WriteTo(fileStream);
}
}
// And then read the bytes back in, to save it to the database
byteArray = File.ReadAllBytes(documentPath); // new bytes, ready for DB saving
Ссылка:
https://docs.microsoft.com/en-us/previous-versions/office/office-12//ee945362(v=office.12)
Но учтите, что даже этот метод потребует сохранить документ, а затем прочитать его обратно, чтобы сохранить его в байтах для базы данных. Также произойдет сбой, если документ имеет формат .doc
вместо .docx
в той строке, где документ открывается.
Вместо этого последнего раздела для сохранения файла в файловой системе вы можете просто взять поток памяти и сохранить его обратно в байтах, как только вы окажетесь за пределами блока WordprocessingDocument.Open()
, но все еще внутри оператора using (MemoryStream mem = new MemoryStream() { ... }
:
// Convert
byteArray = mem.ToArray();
Это будет ваш документ Word byte[]
.