2015-09-05 142 views
0

我想在磁盘上覆盖或创建一个xml文件,并从函数返回xml。我想我可以通过从FileStream复制到MemoryStream来做到这一点。但我最终将一个新的xml文档附加到同一个文件,而不是每次创建一个新文件。 我在做什么错?如果我删除复制,一切工作正常。写入文件流并复制到MemoryStream

public static string CreateAndSave(IEnumerable<OrderPage> orderPages, string filePath) 
    { 
     if (orderPages == null || !orderPages.Any()) 
     { 
      return string.Empty; 
     } 

     var xmlBuilder = new StringBuilder(); 

     var writerSettings = new XmlWriterSettings 
     { 
      Indent = true, 
      Encoding = Encoding.GetEncoding("ISO-8859-1"), 
      CheckCharacters = false, 
      ConformanceLevel = ConformanceLevel.Document 
     }; 

     using (var fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite)) 
     { 
      try 
      { 
       XmlWriter xmlWriter = XmlWriter.Create(fs, writerSettings); 
       xmlWriter.WriteStartElement("PRINT_JOB"); 
       WriteXmlAttribute(xmlWriter, "TYPE", "Order Confirmations"); 

       foreach (var page in orderPages) 
       { 
        xmlWriter.WriteStartElement("PAGE"); 
        WriteXmlAttribute(xmlWriter, "FORM_TYPE", page.OrderType); 

        var outBound = page.Orders.SingleOrDefault(x => x.FlightInfo.Direction == FlightDirection.Outbound); 
        var homeBound = page.Orders.SingleOrDefault(x => x.FlightInfo.Direction == FlightDirection.Homebound); 

        WriteXmlOrder(xmlWriter, outBound, page.ContailDetails, page.UserId, page.PrintType, FlightDirection.Outbound); 
        WriteXmlOrder(xmlWriter, homeBound, page.ContailDetails, page.UserId, page.PrintType, FlightDirection.Homebound); 

        xmlWriter.WriteEndElement(); 
       } 

       xmlWriter.WriteFullEndElement(); 

       MemoryStream destination = new MemoryStream(); 
       fs.CopyTo(destination); 

       Log.Progress("Xml string length: {0}", destination.Length); 

       xmlBuilder.Append(Encoding.UTF8.GetString(destination.ToArray())); 

       destination.Flush(); 
       destination.Close(); 
       xmlWriter.Flush(); 
       xmlWriter.Close(); 
      } 
      catch (Exception ex) 
      { 
       Log.Warning(ex, "Unhandled exception occured during create of xml. {0}", ex.Message); 
       throw; 
      } 

      fs.Flush(); 
      fs.Close(); 
     } 

     return xmlBuilder.ToString(); 
    } 

干杯 延

+0

'FileMode.OpenOrCreate'应该是'FileMode.Create' –

+0

使用LINQ to XML将使得这么多更容易 - 也你不显示你如何实际文件名 – BrokenGlass

+0

决定把它改成FileMode.Create这工作,但现在我没有得到任何XML返回。返回字符串是空的。 –

回答

2

FileMode.OpenOrCreate导致文件内容将被重写而不缩短,从先前运行留下任何“尾随”数据。如果使用FileMode.Create,则该文件将首先被截断。但是,要读回刚写入的内容,您需要使用Seek来重置文件指针。

另外,在从底层流复制之前清空XmlWriter

另请参阅问题Simultaneous Read Write a file in C Sharp (3817477)

下面的测试程序似乎做你想做的事情(减少你自己的日志记录和订单细节)。

using System; 
using System.IO; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Xml; 
using System.Threading.Tasks; 

namespace ReadWriteTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string filePath = Path.Combine(
       Environment.GetFolderPath(Environment.SpecialFolder.Personal), 
       "Test.xml"); 

      string result = CreateAndSave(new string[] { "Hello", "World", "!" }, filePath); 

      Console.WriteLine("============== FIRST PASS =============="); 
      Console.WriteLine(result); 

      result = CreateAndSave(new string[] { "Hello", "World", "AGAIN", "!" }, filePath); 
      Console.WriteLine("============== SECOND PASS =============="); 
      Console.WriteLine(result); 

      Console.ReadLine(); 
     } 

     public static string CreateAndSave(IEnumerable<string> orderPages, string filePath) 
     { 
      if (orderPages == null || !orderPages.Any()) 
      { 
       return string.Empty; 
      } 

      var xmlBuilder = new StringBuilder(); 

      var writerSettings = new XmlWriterSettings 
      { 
       Indent = true, 
       Encoding = Encoding.GetEncoding("ISO-8859-1"), 
       CheckCharacters = false, 
       ConformanceLevel = ConformanceLevel.Document 
      }; 

      using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite)) 
      { 
       try 
       { 
        XmlWriter xmlWriter = XmlWriter.Create(fs, writerSettings); 
        xmlWriter.WriteStartElement("PRINT_JOB"); 

        foreach (var page in orderPages) 
        { 
         xmlWriter.WriteElementString("PAGE", page); 
        } 

        xmlWriter.WriteFullEndElement(); 

        xmlWriter.Flush(); // Flush from xmlWriter to fs 
        xmlWriter.Close(); 

        fs.Seek(0, SeekOrigin.Begin); // Go back to read from the begining 

        MemoryStream destination = new MemoryStream(); 
        fs.CopyTo(destination); 

        xmlBuilder.Append(Encoding.UTF8.GetString(destination.ToArray())); 

        destination.Flush(); 
        destination.Close(); 
       } 
       catch (Exception ex) 
       { 
        throw; 
       } 

       fs.Flush(); 
       fs.Close(); 
      } 

      return xmlBuilder.ToString(); 
     } 
    } 
} 

对于优化器在那里,因为字符串形成整体,并且可以通过只是在一个StreamReader包裹FS可以避免MemoryStreamStringBuilder是不必要的。这将使代码如下。

 public static string CreateAndSave(IEnumerable<string> orderPages, string filePath) 
     { 
      if (orderPages == null || !orderPages.Any()) 
      { 
       return string.Empty; 
      } 

      string result; 

      var writerSettings = new XmlWriterSettings 
      { 
       Indent = true, 
       Encoding = Encoding.GetEncoding("ISO-8859-1"), 
       CheckCharacters = false, 
       ConformanceLevel = ConformanceLevel.Document 
      }; 

      using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite)) 
      { 
       try 
       { 
        XmlWriter xmlWriter = XmlWriter.Create(fs, writerSettings); 
        xmlWriter.WriteStartElement("PRINT_JOB"); 

        foreach (var page in orderPages) 
        { 
         xmlWriter.WriteElementString("PAGE", page); 
        } 

        xmlWriter.WriteFullEndElement(); 

        xmlWriter.Close(); // Flush from xmlWriter to fs 

        fs.Seek(0, SeekOrigin.Begin); // Go back to read from the begining 

        var reader = new StreamReader(fs, writerSettings.Encoding); 
        result = reader.ReadToEnd(); 
        // reader.Close(); // This would just flush/close fs early(which would be OK) 
       } 
       catch (Exception ex) 
       { 
        throw; 
       } 
      } 

      return result; 
     } 
+0

很好的答案,现在工作正常。谢谢你的帮助。 –

1

我知道我迟到了,但似乎有一个更简单的解决方案。你想让你的函数生成xml,将它写入一个文件并返回生成的xml。显然分配一个string是不可避免的(因为你希望它被返回),写入文件也是如此。但是通过简单地“交换”操作可以很容易地避免从文件中读取数据(如你的和SensorSmith的解决方案) - 生成XML字符串并将其写入文件。就像这样:

var output = new StringBuilder(); 
var writerSettings = new XmlWriterSettings { /* your settings ... */ }; 
using (var xmlWriter = XmlWriter.Create(output, writerSettings)) 
{ 
    // Your xml generation code using the writer 
    // ... 
    // You don't need to flush the writer, it will be done automatically 
} 
// Here the output variable contains the xml, let's take it... 
var xml = output.ToString(); 
// write it to a file... 
File.WriteAllText(filePath, xml); 
// and we are done :-) 
return xml; 

重要更新:事实证明,在XmlWriter.Create(StringBuider, XmlWriterSettings)超载忽略来自设置Encoding并始终使用“UTF-16”,所以不要使用这种方法,如果您需要其他编码。

+0

非常漂亮和干净的代码。我会试一试并比较性能。谢谢。 –

+0

好的,我运行了一个比较检查,将10000个订单转储到xml。使用SensorSmiths代码花了1198ms。使用File.WriteAllText花了1108ms。所以不是一个大问题。此外,即使在指定ISO-8859-1时,xml也以UTF-16编码。无论如何感谢输入。 –

+0

关于性能,现在所有这些缓存并不令人惊讶:-)但编码问题真的很烦人,感谢报告,我会在别人读取它时更新答案。保重。 –