[英]System.OutofMemoryException while reading a large text file using C#
[英]OutofMemoryException while reading large XML file in C# windows forms
我一直將.png文件存儲到以下XML元素之一中。 我一直在將圖像文件轉換為base64,然后將該字符串復制到XML。 我還能夠讀取該XML文件並將其加載到我的窗口表單中。 我面臨的問題是隨着XML文件越來越多,節點越來越多,XML文件的大小已經太大,現在是300 MB。 當Windows表單嘗試讀取此大XML文件時,我收到OutOfMemoryExceptions
。 以下是我的xml文件的代碼段。
<TestResult>
<ResultsID>49</ResultsID>
<DateExecuted>2018-02-20T09:36:12.787</DateExecuted>
<UserExecuted>xxx</UserExecuted>
<CorrectedMean>1966.32245</CorrectedMean>
<CorrectedVariance>19525.6632019949</CorrectedVariance>
<TestPassed>true</TestPassed>
<TestResultImage>Qk2.......</TestResultImage>
</TestResult>
我一直在嘗試使用以下代碼將XML加載到.net中
XDocument xmlResultsDoc = XDocument.Load("MeanData.xml");
並存儲到我的模型類中,如下所示。
List<MeanVarianceTestResultDataList =
(xmlResultsDoc.Descendants("TestResult").Select(m => new
MeanVarianceTestResultsData()
{
ResultsID =
Convert.ToInt32(m.Element("ResultsID").Value),
DateExecuted =
Convert.ToDateTime(m.Element("DateExecuted").Value),
UserExecuted =
Convert.ToString(m.Element("UserExecuted").Value),
CorrectedMean =
Convert.ToString(m.Element("CorrectedMean").Value),
CorrectedVariance =
Convert.ToString(m.Element("CorrectedVariance").Value),
TestPassed =
Convert.ToBoolean(m.Element("TestPassed").Value),
TestResultImage =
Convert.FromBase64String(
Convert.ToString(m.Element("TestResultImage").Value))
})).ToList();
對於巨大的xml文件,您必須使用XmlReader來防止內存不足錯誤。 嘗試下面的代碼,該代碼結合使用xml linq和XmlReader
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
while (!reader.EOF)
{
if (reader.Name != "TestResult")
{
reader.ReadToFollowing("TestResult");
}
if (!reader.EOF)
{
XElement testResult = (XElement)XElement.ReadFrom(reader);
string image = (string)testResult.Element("TestResultImage");
}
}
}
}
}
如果XML文件太大而無法一次加載到內存中,則可以使用XmlReader
來流傳輸該文件並一次僅加載一小部分。 此外,對於包含非常大的Base64編碼二進制數據的<TestResultImage>
元素,可以使用XmlReader.ReadElementContentAsBase64(Byte[], Int32, Int32)
增量讀取大塊數據並將其復制到一些Stream
。
以下代碼顯示了如何完成此操作:
//https://stackoverflow.com/questions/49159697/deserializing-json-child-object-values-into-parent-object-using-jsonconvert-dese
public class MeanVarianceTestResultsData
{
public int ResultsID { get; set; }
public DateTime DateExecuted { get; set; }
public string UserExecuted { get; set; }
public string CorrectedMean { get; set; }
public string CorrectedVariance { get; set; }
public bool TestPassed { get; set; }
public string TestResultImageFile { get; set; }
public Stream TestResultImage { get; set; }
}
public static class MeanVarianceTestResultsDataExtensions
{
public static List<MeanVarianceTestResultsData> ReadResultListFrom(XmlReader reader, Func<MeanVarianceTestResultsData, Stream> openStream, Func<Stream, Stream> closeStream)
{
return reader.ReadSubtrees("TestResult").Select(r => ReadResultFrom(r, openStream, closeStream)).ToList();
}
public static MeanVarianceTestResultsData ReadResultFrom(XmlReader reader, Func<MeanVarianceTestResultsData, Stream> openStream, Func<Stream, Stream> closeStream)
{
if (reader == null || openStream == null)
throw new ArgumentNullException();
reader.MoveToContent();
var result = new MeanVarianceTestResultsData();
var isEmpty = reader.IsEmptyElement;
// Read the root
reader.Read();
if (isEmpty)
return result;
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.EndElement)
{
reader.Read();
break;
}
else if (reader.NodeType != XmlNodeType.Element)
// Comment, text, CDATA, etc.
reader.Skip();
else if (reader.Name == "ResultsID")
result.ResultsID = reader.ReadElementContentAsInt();
else if (reader.Name == "DateExecuted")
result.DateExecuted = reader.ReadElementContentAsDateTime();
else if (reader.Name == "UserExecuted")
result.UserExecuted = reader.ReadElementContentAsString();
else if (reader.Name == "CorrectedMean")
result.CorrectedMean = reader.ReadElementContentAsString();
else if (reader.Name == "TestPassed")
result.TestPassed = reader.ReadElementContentAsBoolean();
else if (reader.Name == "TestResultImage")
result.TestResultImage = reader.ReadElementContentAsStream(() => openStream(result), closeStream);
else
reader.Skip();
}
return result;
}
}
public static class XmlReaderExtensions
{
public static Stream ReadElementContentAsStream(this XmlReader reader, Func<Stream> openStream, Func<Stream, Stream> closeStream)
{
if (reader == null || openStream == null)
throw new ArgumentNullException();
Stream stream = null;
try
{
stream = openStream();
byte[] buffer = new byte[4096];
int readBytes = 0;
while ((readBytes = reader.ReadElementContentAsBase64(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, readBytes);
}
}
finally
{
if (closeStream != null && stream != null)
stream = closeStream(stream);
}
return stream;
}
public static IEnumerable<XmlReader> ReadSubtrees(this XmlReader reader, string name)
{
while (reader.ReadToFollowing(name))
{
using (var subReader = reader.ReadSubtree())
yield return subReader;
}
}
}
然后,您可以使用它如下將每個TestResultImage
圖像讀入MemoryStream
:
List<MeanVarianceTestResultsData> results;
using (var reader = XmlReader.Create(fileName))
{
results = MeanVarianceTestResultsDataExtensions.ReadResultListFrom(reader, m => new MemoryStream(), s => { s.Position = 0; return s; });
}
通過完全跳過圖像的中間Base64字符串表示,這將節省大量內存-但每個MemoryStream
仍將使用大量內存。 或者,您可以將圖像流式傳輸到一些臨時文件中以供以后使用,例如,通過執行以下操作:
List<MeanVarianceTestResultsData> results;
using (var reader = XmlReader.Create(fileName))
{
results = MeanVarianceTestResultsDataExtensions.ReadResultListFrom(
reader,
m => { m.TestResultImageFile = Path.GetTempFileName(); return File.Open(m.TestResultImageFile, FileMode.Create); },
s => { s.Dispose(); return null; });
}
在這種情況下,在寫入圖像並將文件名存儲在MeanVarianceTestResultsData
之后,將處理每個流。 (當然,如果您打算在反序列化之后立即處理這些流,則可以將其保持打開狀態。)
樣品提琴 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.