I have an word file that contain my specified pattern text {pattern} and I want to replace those pattern with new my string which was read from database. So I used open xml read stream from my docx template file the replace my pattern string then returned to stream which support to download file without create a temporary file. But when I opened it generated me error on docx file. Below is my example code
public ActionResult SearchAndReplace(string FilePath)
{
MemoryStream mem = new MemoryStream(System.IO.File.ReadAllBytes(FilePath));
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(mem, true))
{
string docText = null;
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd();
}
Regex regexText = new Regex("Hello world!");
docText = regexText.Replace(docText, "Hi Everyone!");
//Instead using this code below to write text back the original file. I write new string back to memory stream and return to a stream download file
//using (StreamWriter sw = new //StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
//{
// sw.Write(docText);
//}
using (StreamWriter sw = new StreamWriter(mem))
{
sw.Write(docText);
}
}
mem.Seek(0, SeekOrigin.Begin);
return File(mem, "application/octet-stream","download.docx"); //Return to download file
}
Please suggest me any solutions instead read a text from a word file and replace those expected pattern text then write data back to the original file. Are there any solutions replace text with WordprocessingDocument libary? How can I return to memory stream with validation docx file format?
The approach you are taking is not correct. If, by chance, the pattern you are searching for matches some Open XML markup, you will corrupt the document. If the text you are searching for is split over multiple runs, your search/replace code will not find the text and will not operate correctly. If you want to search and replace text in a WordprocessingML document, there is a fairly easy algorithm that you can use:
- Break all runs into runs of a single
character. This includes runs that
have special characters such as a
line break, carriage return, or hard
tab.
- It is then pretty easy to find a
set of runs that match the characters
in your search string.
- Once you have identified a set of runs that match,
then you can replace that set of runs
with a newly created run (which has
the run properties of the run
containing the first character that
matched the search string).
- After replacing the single-character runs
with a newly created run, you can
then consolidate adjacent runs with
identical formatting.
I've written a blog post and recorded a screen-cast that walks through this algorithm.
Blog post: http://openxmldeveloper.org/archive/2011/05/12/148357.aspx
Screen cast: http://www.youtube.com/watch?v=w128hJUu3GM
-Eric
Writing directly to the word document stream will indeed corrupt it.
You should instead write to the MainDocumentPart
stream, but you should first truncate it.
It looks like MainDocumentPart.FeedData(Stream sourceStream)
method will do just that.
I haven't tested it but this should work.
public ActionResult SearchAndReplace(string FilePath)
{
MemoryStream mem = new MemoryStream(System.IO.File.ReadAllBytes(FilePath));
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(mem, true))
{
string docText = null;
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd();
}
Regex regexText = new Regex("Hello world!");
docText = regexText.Replace(docText, "Hi Everyone!");
using (MemoryStream ms = new MemoryStream())
{
using (StreamWriter sw = new StreamWriter(ms))
{
sw.Write(docText);
}
ms.Seek(0, SeekOrigin.Begin);
wordDoc.MainDocumentPart.FeedData(ms);
}
}
mem.Seek(0, SeekOrigin.Begin);
return File(mem, "application/octet-stream","download.docx"); //Return to download file
}
string sourcepath = HttpContext.Server.MapPath("~/File/Form/s.docx");
string targetPath = HttpContext.Server.MapPath("~/File/ExportTempFile/" + DateTime.Now.ToOADate() + ".docx");
System.IO.File.Copy(sourcepath, targetPath, true);
using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(targetPath, true))
{
string docText = null;
using (StreamReader sr = new StreamReader(wordDocument.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd();
}
Regex regexText = new Regex("Hello world!");
docText = regexText.Replace(docText, "Hi Everyone!");
byte[] byteArray = Encoding.UTF8.GetBytes(docText);
MemoryStream stream = new MemoryStream(byteArray);
wordDocument.MainDocumentPart.FeedData(stream);
}
MemoryStream mem = new MemoryStream(System.IO.File.ReadAllBytes(targetPath));
return File(mem, "application/octet-stream", "download.docx");