开发者

Only count a download once it's served

开发者 https://www.devze.com 2023-04-07 07:31 出处:网络
We have this code which serves a download: public class downloadRelease : IHttpHandler { public void ProcessRequest (HttpContext context) {

We have this code which serves a download:

public class downloadRelease : IHttpHandler {

    public void ProcessRequest (HttpContext context) {

        -- snip --

        context.Response.Clear();
        context.Response.ContentType = "application/octet-stream";
        context.Response.AddHeader("Content-Disposition", "attachment; filename=" + OriginalFil开发者_开发技巧eName);
        context.Response.WriteFile(Settings.ReleaseFileLocation + ActualFileName);

        // Log download
        Constructor.VersionReleaseDownload.NewReleaseDownload(ActualFileName);

It works fine, except that the log download code runs as soon as the download starts seemingly, not when the download has fully completed as we expect.

Can someone explain why this is, and how to change it so it only logs when it's completed? We don't want to count partial downloads.


This blog post has exactly same issue as yours and a solution too.

Response.Buffer = false;
Response.TransmitFile("Tree.jpg");
Response.Close();
// logging here


The write response is an asynchronous process. It is managed by the container for the application. In your case the .NET/ASP.net run time is handling it. If you want to find out when the last chunk was sent you'll have to have some kind of callback/event on that [coming from the container/runtime]. In Java its the application server that gets this information [Glassfish, Tomcat etc]


You can try to add this before writing the file:

context.Response.BufferOutput = false;


Have you tried handling the EndRequest event of HttpApplication?

Or perhaps, using the ReleaseHandler() method of IHttpHandlerFactory, assuming that you mark your IHttpHandler as non-reusable?


This is a bit tricky... depending on how precise you want the logging to be it might even be that it is not possible... but there are the following options with the Response object :

  • BinaryWrite / Flush / Close

  • TransmitFile / Flush / Close

The first option requires you to read the file chunk after chunk calling BinaryWrite and Flush for every chunk...

The second option is easier to implement since it is just a call to TransmitFile and then to Flush.

After everything is sent and before logging you need to call Close.

In any case it can help to call DisableKernelCache before starting to send a response...

BEWARE that all of the above will show a noticeable performance hit! This effect can be reduced by creating an in-memory-cache for the files you want to serve though...

As to the logging I would consider moving the logging code to the EndRequest event handler...

AFAIK this is the nearest you can get to your goal except writing your own TcpListener-based HTTP server or hacking IIS / HTTP.SYS .

Some reference links:

  • http://msdn.microsoft.com/en-us/library/12s31dhy.aspx
  • http://msdn.microsoft.com/en-us/library/system.web.httpresponse.binarywrite.aspx
  • http://msdn.microsoft.com/en-us/library/system.web.httpresponse.flush.aspx
  • http://msdn.microsoft.com/en-us/library/system.web.httpresponse.close.aspx
  • http://msdn.microsoft.com/en-us/library/system.web.httpresponse.disablekernelcache.aspx
  • http://msdn.microsoft.com/en-us/library/system.web.httpapplication.endrequest.aspx
0

精彩评论

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