Only count a download once it's served
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
精彩评论