我必须使用WCF通过不可靠的连接在计算机之间传输大文件。
因为我希望能够恢复该文件,并且我不想被WCF限制在我的文件大小中,所以我将这些文件分块为1MB。这些“块”以流的形式传输。到目前为止,哪个效果很好。
我的步骤是:
我的问题在第2步。我假设当我从字节数组创建一个内存流时,它将最终在LOH上并最终导致outofmemory异常。我实际上无法创建此错误,也许我的假设是错误的。
现在,我不想在消息中发送byte [],因为WCF会告诉我数组大小太大。我可以更改允许的最大数组大小和/或我的块的大小,但我希望有另一种解决方案。
我的实际问题:
顺便说一句:在接收端,我只是从到达的流中读取较小的块并将它们直接写入文件,因此不涉及大字节数组。
编辑:
目前的解决方案:
for (int i = resumeChunk; i < chunks; i++)
{
byte[] buffer = new byte[chunkSize];
fileStream.Position = i * chunkSize;
int actualLength = fileStream.Read(buffer, 0, (int)chunkSize);
Array.Resize(ref buffer, actualLength);
using (MemoryStream stream = new MemoryStream(buffer))
{
UploadFile(stream);
}
}
答案 0 :(得分:35)
我希望这没关系。这是我在StackOverflow上的第一个答案。
是的,如果你的chunksize超过85000字节,那么数组将在大对象堆上分配。您可能不会很快耗尽内存,因为您正在分配和释放大小相同的连续内存区域,因此当内存填满运行时可以将新块放入旧的回收内存区域。
我会对Array.Resize调用有点担心,因为这会创建另一个数组(参见http://msdn.microsoft.com/en-us/library/1ffy6686(VS.80).aspx)。如果actualLength == Chunksize,这是一个不必要的步骤,因为除了最后一个块之外,它将是所有的。所以我至少建议:
if (actualLength != chunkSize) Array.Resize(ref buffer, actualLength);
这应该删除大量的分配。如果actualSize与chunkSize不同但仍然是> 85000然后新的数组也将在Large对象堆上分配,可能导致它碎片并可能导致明显的内存泄漏。我相信它仍然需要很长时间才能实现内存不足,因为泄漏会很慢。
我认为更好的实现方法是使用某种缓冲池来提供数组。你可以自己动手(这太复杂了)但是WCF确实为你提供了一个。我稍微重写了你的代码,以便对此有所了解:
BufferManager bm = BufferManager.CreateBufferManager(chunkSize * 10, chunkSize);
for (int i = resumeChunk; i < chunks; i++)
{
byte[] buffer = bm.TakeBuffer(chunkSize);
try
{
fileStream.Position = i * chunkSize;
int actualLength = fileStream.Read(buffer, 0, (int)chunkSize);
if (actualLength == 0) break;
//Array.Resize(ref buffer, actualLength);
using (MemoryStream stream = new MemoryStream(buffer))
{
UploadFile(stream, actualLength);
}
}
finally
{
bm.ReturnBuffer(buffer);
}
}
这假设可以重写UploadFile的实现以获取no的int。要写的字节数。
我希望这会有所帮助
乔
答案 1 :(得分:5)
另见RecyclableMemoryStream。 来自this article:
Microsoft.IO.RecyclableMemoryStream是一个MemoryStream替代品,可为性能关键系统提供卓越的性能。特别是它被优化以执行以下操作:
答案 2 :(得分:2)
我对你问题的第一部分不太确定,但至于更好的方法 - 你考虑过BITS吗?它允许通过http下载文件的后台。您可以提供http://或file:// URI。它可以从中断的角度恢复,并使用http HEADER中的RANGE方法以字节块的形式下载。它由Windows Update使用。您可以订阅提供有关进度和完成信息的事件。
答案 3 :(得分:1)
我已经为此提出了另一个解决方案,让我知道你的想法!
由于我不想在内存中包含大量数据,因此我一直在寻找一种优雅的方法来临时存储字节数组或流。
我们的想法是创建一个临时文件(您不需要特定的权限来执行此操作),然后使用它类似于内存流。使类Disposable将在使用后清除临时文件。
public class TempFileStream : Stream
{
private readonly string _filename;
private readonly FileStream _fileStream;
public TempFileStream()
{
this._filename = Path.GetTempFileName();
this._fileStream = File.Open(this._filename, FileMode.OpenOrCreate, FileAccess.ReadWrite);
}
public override bool CanRead
{
get
{
return this._fileStream.CanRead;
}
}
// and so on with wrapping the stream to the underlying filestream
...
// finally overrride the Dispose Method and remove the temp file
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
this._fileStream.Close();
this._fileStream.Dispose();
try
{
File.Delete(this._filename);
}
catch (Exception)
{
// if something goes wrong while deleting the temp file we can ignore it.
}
}