Как я могу сформировать документ Word, используя поток байтов - PullRequest
4 голосов
/ 14 августа 2011

У меня есть поток байтов, который фактически (если исправить) сформирует правильный файл Word, мне нужно преобразовать этот поток в файл Word, не записывая его на диск, я беру исходный поток из таблицы базы данных SQL Server:

ID   Name    FileData
----------------------------------------
1    Word1   292jf2jf2ofm29fj29fj29fj29f2jf29efj29fj2f9 (actual file data)

поле FileData содержит данные.

Microsoft.Office.Interop.Word.Application word = new Microsoft.Office.Interop.Word.Application();
Microsoft.Office.Interop.Word.Document doc = new Microsoft.Office.Interop.Word.Document(); 
doc = word.Documents.Open(@"C:\SampleText.doc");
doc.Activate();

Приведенный выше код открывает и заполняет файл Word из файловой системы, я не хочу этого, я хочу определитьnew Microsoft.Office.Interop.Word.Document, но я хочу заполнить его содержимое вручную из байтового потока.

После получения документа Word в памяти я хочу выполнить несколько разборов ключевых слов.

Есть идеи?

Ответы [ 4 ]

0 голосов
/ 19 ноября 2018

На самом деле есть только 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[].

0 голосов
/ 14 августа 2011

Вероятно, нет никакого прямого способа сделать это.Я нашел пару решений, ищущих его:

Я не знаю, делает ли это это для вас, но, очевидно, API не обеспечивает то, что вы ищете (к сожалению).

0 голосов
/ 15 августа 2011

Вы можете посмотреть, как Sharepoint решает это.Они создали веб-интерфейс для документов, хранящихся в их базе данных.

Не так сложно создать или встроить веб-сервер в свое приложение, которое может обслуживать страницы в Word.Вам даже не нужно использовать стандартные порты.

0 голосов
/ 14 августа 2011
  1. Создайте файловую систему в памяти, для этого есть драйверы.
  2. Укажите слово путь к файлу FTP-сервера (или что-то еще), который вы затем используете для передачи данных.

Важное замечание: хранение файлов в базе данных, как правило, не очень хороший дизайн.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...