打开xml替换word文件中的文本并使用MVC返回内存流

时间:2011-04-15 02:12:14

标签: asp.net-mvc openxml docx openxml-sdk

我有一个包含我指定的模式文本{pattern}的word文件,我想用从数据库中读取的新字符串替换这些模式。所以我使用了来自我的docx模板文件的open xml读取流替换我的模式字符串然后返回流,支持下载文件而不创建临时文件。但是当我打开它时会在docx文件上生成错误。下面是我的示例代码

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
}

请建议我解决任何解决方案,而不是从word文件中读取文本并替换那些预期的模式文本,然后将数据写回原始文件。是否有任何解决方案用WordprocessingDocument libary替换文本?如何使用验证docx文件格式返回内存流?

3 个答案:

答案 0 :(得分:2)

您采取的方法不正确。如果您正在搜索的模式偶然与某些Open XML标记匹配,则会损坏文档。如果您要搜索的文本在多次运行中分割,则您的搜索/替换代码将找不到文本,并且无法正常运行。如果要在WordprocessingML文档中搜索和替换文本,可以使用相当简单的算法:

  • 将所有游戏分解为单个游戏 字符。这包括运行 有特殊字符,如a 换行,回车或辛苦 标签。
  • 然后很容易找到一个 与角色匹配的一组运行 在你的搜索字符串中。
  • 一旦确定了一组匹配的运行, 然后你可以替换那组运行 使用新创建的运行(具有 运行的运行属性 包含第一个字符 匹配搜索字符串)。
  • 更换单字符后运行 使用新创建的运行,您可以 然后巩固相邻的运行 格式相同。

我写了一篇博客文章并录制了一个屏幕演员,其中介绍了这个算法。

博文:http://openxmldeveloper.org/archive/2011/05/12/148357.aspx
屏幕投射:http://www.youtube.com/watch?v=w128hJUu3GM

-Eric

答案 1 :(得分:1)

直接写入word文档流确实会破坏它。 您应该写入MainDocumentPart流,但应首先截断它。 看起来MainDocumentPart.FeedData(Stream sourceStream)方法就是这样做的。

我没有测试过,但这应该有用。

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
}

答案 2 :(得分:1)

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");