3

ユーザーが URL からファイルをダウンロードできるようにするライブラリを構築しています。私が検討しているオプションの 1 つは、ユーザーがファイルの予想される MD5 チェックサムを指定できるようにすることです。ライブラリの GetFile(string url) 関数は、ダウンロードされたストリームのチェックサムがユーザーによって指定されたものと一致することを保証します。

HttpWebResponse.GetResponseStream() によって返される NetworkStream がシーク可能ではないことに注意して、この質問への回答のおかげでストリームを複製する方法を見つけました: How can I read an Http response stream once in C#? . 先に進む前に、この重複がメモリにどのような影響を与えるかを理解したいと思いました。残念ながら、Google と MSDN で何度も検索しても無駄になりました。

ライブラリは、ダウンロードするファイルのサイズに制限を課しません。私の質問は、ユーザーが 2 GB のファイルを選択した場合、.NET 2.0 の MemoryStream 実装は、VM クランチのためにシステムがクロールを開始しないほど十分に効率的に PageFile と RAM を使用するのに十分スマートですか? また、別の質問に対する Jon Skeet のコメントは、私に考えさせてくれました。彼は、MemoryStream を破棄した後でも、メモリが 100% 解放されていないことを断言しました。メモリが実際に解放されることをいつどのように確認できますか? システムの要件 (および必要性) に基づいてリリースされますか?

ありがとう、マノジ

4

4 に答える 4

5

ファイルに保存していますよね?チャンクごとに保存し、ハッシュを更新し、最後にハッシュをチェックしてみませんか? 応答を 2 回読み取る必要も、バッファリングする必要もないと思います。別の回答が指摘しているように、とにかく 1GB を超えると失敗します。

MemoryStreamの現在のサイズと同様に、それが大きくなる必要があるときはいつでも、(一時的に) 新しい配列メモリ内の古い配列が同時に発生することを忘れないでください。もちろん、コンテンツの長さが事前にわかっていれば問題ありませんが、ディスクに書き込んでハッシュするだけの方がよいでしょう。

于 2009-10-02T20:30:04.070 に答える
4

MemoryStream は配列によって支えられています。64 ビット OS を使用している場合でも、フレームワークがより大きな配列を割り当てないため、これは 1GB 以上では機能しません。

于 2009-10-02T20:35:40.363 に答える
2

Afaik CLR マネージ ヒープは 2 GB を超えるものを割り当てず、MemoryStream はライブの連続した byte[] によってサポートされます。大きなオブジェクト ヒープは、x64 でも、2 GB を超える割り当てを処理しません。

しかし、ハッシュを計算するためだけにファイル全体をメモリに保存するのは、かなりローテクに思えます。バイトを受信すると、チャンクごとにハッシュを計算できます。IO が完了するたびに、受信したバイトをハッシュしてから、書き込みをファイルに送信できます。最後に、ハッシュが計算さ、ファイルがアップロードされます。

ところで、ファイルを操作するコードを探す場合は、次のような単語を含むサンプルを避けてくださいReadToEnd...

class Program
    {
        private static AutoResetEvent done = new AutoResetEvent(false);
        private static AsyncCallback _callbackReadStream;
        private static AsyncCallback _callbackWriteFile;

        static void Main(string[] args)
        {

        try
        {
            _callbackReadStream = new AsyncCallback(CallbackReadStream);
            _callbackWriteFile = new AsyncCallback(CallbackWriteFile);
            string url = "http://...";
            WebRequest request = WebRequest.Create(url);
            request.Method = "GET";
            request.BeginGetResponse(new AsyncCallback(
                CallbackGetResponse), request);
            done.WaitOne();
        }
        catch (Exception e)
        {
            Console.Error.WriteLine(e.Message);
        }
    }

    private class State
    {
        public Stream ReponseStream { get; set; }
        public HashAlgorithm Hash { get; set; }
        public Stream FileStream { get; set; }
        private byte[] _buffer = new byte[16379];
        public byte[] Buffer { get { return _buffer; } }
        public int ReadBytes { get; set; }
        public long FileLength {get;set;}
    }

    static void CallbackGetResponse(IAsyncResult ar)
    {
        try
        {
            WebRequest request = (WebRequest)ar.AsyncState;
            WebResponse response = request.EndGetResponse(ar);

            State s = new State();
            s.ReponseStream = response.GetResponseStream();
            s.FileStream = new FileStream("download.out"
                , FileMode.Create
                , FileAccess.Write
                , FileShare.None);
            s.Hash = HashAlgorithm.Create("MD5");

            s.ReponseStream.BeginRead(
                s.Buffer
                , 0
                , s.Buffer.Length
                , _callbackReadStream
                , s); 
        }
        catch (Exception e)
        {
            Console.Error.WriteLine(e.Message);
            done.Set();
        }
    }

    private static void CallbackReadStream(IAsyncResult ar)
    {
        try
        {
            State s = (State)ar.AsyncState;
            s.ReadBytes = s.ReponseStream.EndRead(ar);
            s.Hash.ComputeHash(s.Buffer, 0, s.ReadBytes);
            s.FileStream.BeginWrite(
                s.Buffer
                , 0
                , s.ReadBytes
                , _callbackWriteFile
                , s);
        }
        catch (Exception e)
        {
            Console.Error.WriteLine(e.Message);
            done.Set();
        }
    }

    static private void CallbackWriteFile(IAsyncResult ar)
    {
        try
        {
            State s = (State)ar.AsyncState;
            s.FileStream.EndWrite(ar);

            s.FileLength += s.ReadBytes;

            if (0 != s.ReadBytes)
            {
                s.ReponseStream.BeginRead(
                    s.Buffer
                    , 0
                    , s.Buffer.Length
                    , _callbackReadStream
                    , s);
            }
            else
            {
                Console.Out.Write("Downloaded {0} bytes. Hash(base64):{1}",
                    s.FileLength, Convert.ToBase64String(s.Hash.Hash));
                done.Set();
            }
        }
        catch (Exception e)
        {
            Console.Error.WriteLine(e.Message);
            done.Set();
        }

    }
}
于 2009-10-02T20:46:28.703 に答える
0

OutOfMemoryException が発生することは間違いありません。簡単な方法は、メモリ ストリームを使用して DVD ISO イメージなどをメモリに読み込んでみることです。全部読めるなら大丈夫です。例外が発生した場合は、問題ありません。

于 2009-10-02T20:28:46.927 に答える