开发者

Streaming a File with .NET - Client Connection Time?

开发者 https://www.devze.com 2022-12-21 13:04 出处:网络
So I asked a question a while back about securing downloads using C# (Securing Large Downloads Using C# and IIS 7) and I got some great advice on how to do it (involving reading the file into memory a

So I asked a question a while back about securing downloads using C# (Securing Large Downloads Using C# and IIS 7) and I got some great advice on how to do it (involving reading the file into memory and then writing it to the user). The only problem is, now that I'm trying to implement some basic logging, I'm hitting a brick wall. Here's the code to stream a file:

public void StreamFile(string file_path)
{
    DateTime start;
    TimeSpan ts;
    FileStream fstream;
    string filename = Path.GetFileName(file_path);
    byte[] buffer = new byte[STREAM_BUFFER_SIZE];
    int count = 1, total = 0, seconds;

    // Open the file to read
    fstream = new FileStream("D:\\" + file_path, FileMode.Open, FileAccess.Read);

    // Set up the response headers
    Response.AddHeader("Content-Length", fstream.Length.ToString());
    Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
    Response.ContentType = "application/octet-stream";

    // If the user agent is Internet Explorer, we add one last header
    if (Request.UserAgent.Contains("MSIE"))
        Response.AddHeader("Content-Transfer-Encoding", "binary");

    // Start counting the time
    start = DateTime.Now;

    // Now, until the client disconnects, we stream the file
    while (Response.IsClientConnected)
    {
        // Read the file into the buffer
        count = fstream.Read(buffer, 0, buffer.Length);

        // If the buffer is empty, break out of the loop. We're done!
        if (count == 0)
            break;

        // Write to the output stream and push it to the user
        Response.OutputStream.Write(buffer, 0, count);
        Response.Flush();

        // Increment the total as well, this way we can know how much we've streamed
        total += count;
    }
    // The transfer is done! Close the connection.
    Response.Close();

    // Count the number of seconds
    ts = DateTime.Now - start;
    seconds = ts.Seconds + (60 * ts.Minutes) + (60 * 60 * ts.Hours); // Also, is there a better way to do this? This is laaaaaame!

    // Finally, log the transfer
    Logging.AddLog(Request["user"], file_path, total, count == 0, seconds);
}

Ok, so the problem is, the log entries that this is creating say that the file completed in the number of seconds it took to read the file into memory, not for the user to download it. I assumend that Response.ClientConnected would take care of that, but apparently not. So the time to download the file as reported by the logs is 0-1 seconds, and e开发者_如何学Cven when I stop a download part way through, the logs say it provided the whole thing.

Anyone done anything like this before, or any ideas how I can get the REAL numbers behind the transfer? Having this information is a huge priority, unfortunately, or I'd just shrug it off and remove those two values from the logging altogether.


I'm sorry, but it's probably not possible to do what you want to do. After you call Write() (on the NetworkStream), the data is immediately given to Windows' Sockets, which in turn writes the data to the buffer of the network card. So, .Net doesn't do any buffering and Write returns immediately. Because a NetworkStream is not buffered by .Net, calling Flush() has no effect. Therefore, from .Net it is not possible to wait until the data has left the network card's buffer.

Even if you, somehow, managed to wait exactly until the data has left the NIC's buffer, there is no guarantee that the client ever received the data.


The solution is to have your client application send a "Received" confirmation packet back once it has the whole file. Once you receive that, you know they actually received the whole thing, and can stop the timer.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号