我已经使用了多年来通过HttpWebRequest POST请求上传XML和TIF文件对的代码。问题是,在大型TIF文件上,它像一群攻击森林的海狸一样通过记忆咀嚼。我今天开始深入研究代码,试图提高内存效率。
现有代码将XML和TIF内容加载到字符串对象中,然后将其转换为字节数组并送入HTTP请求。整个过程涉及许多字符串连接。加载TIF文件并将其转换为这样的字符串对象,其中br2是BinaryReader对象:
System.Text.Encoding.Default.GetString(br2.ReadBytes(tifByteCount))
我现在知道使用Encoding.Default并不明智,但是改变它将需要与客户端一起改变对文件提交的解码,这是另一次。当我进行更改时,我可能会更改为base64编码。总之...
我改变的第一个项目是我的所有字符串连接,因为我认为这会让事情变得迟钝,特别是在处理TIF字符串对象时。我现在正在使用StringBuilder对象并附加所有内容。
然后我搜索了“字节数组到字符串转换”并尝试了我找到的几个不同的结果,包括this one和this one,但两者都使用了与现有代码不同的编码。
然后我使用System.Text.Encoding.Default.Decoder对象将整个TIF文件一次解码为char []数组。这根本没有改善内存,但至少使用了相同的编码。
我今天一直在测试的文件是一个185 MB的TIF文件。在我的开发机器上进行测试时,我的Windows物理内存使用量将从2 GB开始使用,并且很快将升至5 GB以上,然后最大值为5.99 GB并立即锁定,直到调试器自行终止。据我所知,我只是将一个TIF文件实例加载到内存中,所以我无法理解为什么185 MB占用4 GB内存。
无论如何,接下来我尝试在更小的块中加载TIF文件。一次1000个字节。这看起来很有希望。在加载除文件的最后<1000字节之外的所有内容时,它仅使用2 GB内存。在最后一块字节(在这种情况下为928字节),此行charCount = dc.GetCharCount(ba2, x, (int)fileStream2.Length - x)
导致内存暂时增加1 GB,后续行chars2 = new Char[(int)fileStream2.Length - x]
将内存增加700 MB,并且以下行charsDecodedCount = dc.GetChars(ba2, x, (int)fileStream2.Length - x, chars2, 0)
将内存推到最大并锁定了系统。
下面的代码显示了最后尝试的方法 - 上一段中描述的方法。
BinaryReader br2 = new BinaryReader(fileStream2);
byte[] ba2 = br2.ReadBytes((int)fileStream2.Length);
Char[] chars2 = null;
if ((int)fileStream2.Length > 1000)
{
for (int x = 0; x < (int)fileStream2.Length; x += 1000)
{
if (x + 1000 > (int)fileStream2.Length)
{
charCount = dc.GetCharCount(ba2, x, (int)fileStream2.Length - x);
chars2 = new Char[(int)fileStream2.Length - x];
charsDecodedCount = dc.GetChars(ba2, x, (int)fileStream2.Length - x, chars2, 0);
}
else
{
charCount = dc.GetCharCount(ba2, x, 1000);
chars2 = new Char[charCount];
charsDecodedCount = dc.GetChars(ba2, x, 1000, chars2, 0);
}
sbRequest.Append(chars2);
chars2 = null;
}
}
else
{
charCount = dc.GetCharCount(ba2, 0, ba2.Length);
chars2 = new Char[charCount];
charsDecodedCount = dc.GetChars(ba2, 0, ba2.Length, chars2, 0);
sbRequest.Append(chars2);
}
我有一种感觉,我错过了一些相当明显的东西。我很感激任何解决这个问题的建议。我希望能够在不使用4 GB内存的情况下加载185 MB TIF文件!
答案 0 :(得分:2)
您当前代码中的几个主要问题:
byte[] ba2 = br2.ReadBytes((int)fileStream2.Length);
这会将整个文件读取到内存中。
dc.GetCharCount(...)
dc.GetChars(...)
这些方法使用内部缓冲区,因此它们会增加内存使用量,就像你说的那样。
你不是&#34;在TIF文件中一次加载1000个字节的小字节&#34; 。您将整个文件加载到内存中,并一次解码1000字节的字节。
如果你真的想让你的方法使用尽可能少的内存,我建议只使用流。这是一个例子:
using (var fs = new FileStream("tif file", FileMode.Open))
{
var request = (HttpWebRequest)WebRequest.Create("address");
request.Method = "POST";
request.ContentLength = fs.Length;
using (Stream postStream = request.GetRequestStream())
{
// Write the other contents you wanted to write here
// ...
// CopyTo uses a buffer of 4096 bytes by default, so it will
// only read 4096 bytes into memory at a time.
fs.CopyTo(postStream);
postStream.Close(); // Not sure if necessary since we're in a using block
}
using (HttpWebResponse response = request.GetResponse()) // might need to cast to HttpWebRequest
using (Stream responseStream = response.GetResponseStream())
using (var streamReader = new StreamReader(responseStream))
{
string response = Encoding.UTF8.GetString(streamReader.ReadToEnd());
// Stuff with the response
}
}
通过在其构造函数中指定FileOptions.SequentialScan
,您可以从大型读取文件流中获得更好的性能。此标志&#34;表示要从头到尾依次访问该文件。系统可以使用它作为优化文件缓存的提示。&#34; [1]
您可以找到有关该标志here的详细信息。
using (var fs = new FileStream("tif file", FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialSeek))
答案 1 :(得分:0)
使用MIME multipart可以轻松地在单个请求中发布TIFF和XML文档。请参阅下面的示例,其内存使用量以千字节为单位 - 无论文件大小如何:
var content = new MultipartFormDataContent();
var tiffFile = new StreamContent(File.OpenRead("demo.tiff"));
tiffFile.Headers.ContentType = new MediaTypeHeaderValue("image/tiff");
content.Add(tiffFile, "image");
var xml = "<x>foo</x>";
var xmlContent = new StringContent(xml, Encoding.UTF8, "application/xml");
content.Add(xmlContent, "metadata");
var response = (new HttpClient()).PostAsync("http://target/service", content).Result;
response.EnsureSuccessStatusCode();
这会将内容发布到服务器:
POST http://target/service HTTP/1.1
Content-Type: multipart/form-data; boundary="5c3654f8-8e3c-4454-921a-36e0f7761265"
Host: target
Content-Length: 157289445
Expect: 100-continue
Connection: Keep-Alive
--5c3654f8-8e3c-4454-921a-36e0f7761265
Content-Type: image/tiff
Content-Disposition: form-data; name=image
«TIFF Data goes here»
--5c3654f8-8e3c-4454-921a-36e0f7761265
Content-Type: application/xml; charset=utf-8
Content-Disposition: form-data; name=metadata
<x>foo</x>
--5c3654f8-8e3c-4454-921a-36e0f7761265--