views:

393

answers:

5

I am trying to design a system for something like this with ASP.net/C#.

The users pay for downloading some content (files- mp3s/PDFs,doc etc).I should be able to track the number of bytes downloaded by the user. If the number of bytes downloaded match the number of bytes on the server, I should set a flag in DB (telling that the download was successful and prevent them from downloading the file again/asking them to pay for the download again). If the download was incomplete, they should be able to download the file again without paying for it again(since the flag will not be set).

Is there any way to keep track of the number of bytes successfully downloaded by the client ?

Also when I see a file size in my WinXP machine, I see two sizes(size,size on disk). Which one should I consider ? And will it differ from one OS to another ?

+1  A: 

You could try looking into HTTP reponse codes (i.e: 200, 404 etc) - the client and server will be exchanging http headers so that they know what's going on - you should be able to monitor these to see if the reponses was successful (not sure - but you should be able to).

With regards to file size - I would try experiments on files with 'known' sizes, compare what the Http Logs tell you with what file explorer tells you.

Also, I've seen tools/wodgets that report file upload progress - so you're right you should be able to to the same in reverse, I guess. You could try looking at file upload code examples and tutorials - you might get some hints. I can't think of any off the top of my head - sorry.

Adrian K
+1 I also thought something like this, but the bit that isn't clear for me is how you'd go about checking those with minimal overhead throughout all actions. In my case, I used an asp.net mvc actionresult, for different reasons.
eglasius
the client browser gets the response codes. The Server sends the response codes,the response code can still be 200, but the client might disconnect when downloading the file, would you agree?
ram
Obviously the issue with looking at upload code is that the relative responsibilities between the client and server are reversed - so it might not be that helpful, but following such Iideas has helped me in the past.
Adrian K
@Ram: I would agree. You could jhave the request initiated by some JavaScript code - could that ensure the download was successful by acting as a proxy on behalf of the overal process? The 2 big problems with that are (1) it's not a simple / KISS solution, (2) you then have to 'block' calls to the files from other sources.
Adrian K
+1  A: 

you can create an asp.net handler that serves the file ( for asp.net mvc u can do a result action instead ... this is what I'm using). Make sure it supports resumable downloads.

from the you can track the bytes served.

Ps. this incurs a performance overhead vs. letting IIS serve it

update 1: I used something pretty similar to this http://dotnetslackers.com/articles/aspnet/Range-Specific-Requests-in-ASP-NET.aspx ... and the article has a pretty clear explanation on what's inside it. You probably can use that one as is, see the example in that post.

eglasius
can you point me to an example ?
ram
@ram just added it as an update.
eglasius
A: 

The size you want is the size (not the size on disk). Size on disk includes extra space that is taken up by fitting into the 4K block size of the partition. The size is the exact number of bits in the file.

I don't believe there is a good way to tell that a download has been completed. Response.TransmitFile is probably the best method for sending the file securely. But I don't believe it has anything that will tell you if the user actually recieved the file.

I don't know about the business this is supporting, but I can't think of a legitimate business where users would tolerate a single download per purchase model, and with the abiguity of the standard HTTP request/response model does not lend itself to making an accurate client side reciever. Not to mention this model could be eaisyly hacked by sending a failed response on reciept of the last packet.

I think using somthing like download windows (2hrs after purchase) and then lock it to an IP after the first request would accomplish the same result and result in alot less user issues and support calls. Also unless the file has some sort of stringent DRM, allowing the user persisten access based on their loggin is most likely the appropriate business model, because once they get the file they can copy it as many times as they like.

Look at DVD or Blu-Ray, no amount of copy protection or access controls will save your files from pirates, so make things easy for legitimate users.

Joel Barsotti
+7  A: 

You can easily measure data passed to the client in ASP.NET assuming you replace a direct IIS-controlled download with your own, which would go something like this:

while (context.Response.IsClientConnected) {

    bytesRead = ReadFileChunkAsByteArrayWIthOffsetOrWhatever(buffer, offset);

    context.Response.OutputStream.Write(buffer, 0, bytesRead);
    context.Response.Flush();

    offset += bytesRead;

    if (bytesRead != bufferSize)
        break;
}

It's complicated to make this 100% reliable from within ASP, but it can be done. You pretty much have to account for every possible failure point and react accordingly.

The problem though is still - as someone mentioned above - that it's impossible to know that the client received the data. If money is involved in this transaction, that can get to be a problem really quickly.

For that reason, the best approach would be to use a custom downloader client, like the one Amazon uses for MP3 file purchases. That way you're not subjecting either yourself or your customers to the vagaries of moving monetized bits over something as unreliable as HTTP.

kprobst
+1 custom client side downloader seems like the only viable solution.
Martijn Laarman
Reading the File into memory, if you are doing alot of these operations is going to cause a huge perfomance issue and will not scale well. The result of this is that you will throw outOfMemoryExceptions. What you want to do is use Response.TransferFile(string fileName), or the Respone.Transfer(string fileName, long offset, long lenght) method.
Joel Barsotti
A: 

To do custom byte serving like this, you will need to implement your own http handler.

This handler should do the following:

  • Implement some kind of authentication on the http handler, so you know who you are dealing with.
  • Then you will need to implement some kind of logging for files requested and files allowed to be downloaded.
  • Implement etags and expires headers for client side caching.
  • Server side caching
  • Deflate, gzip compression
  • If you want to support resumable downloads, you will need to implement 206 partial responses. This is essential for any kind of streaming and serving pdfs.

So you should be handling the following http headers:

  • ETag
  • Expires

  • Accept-Ranges

  • Range
  • If-Range

  • Last-Modified

  • If-Match
  • If-None-Match
  • If-Modified-Since
  • If-Unmodified-Since
  • Unless-Modified-Since

If you are looking for a sample implementations of http handlers check out: http://code.google.com/p/talifun-web/wiki

It has a static file handler that implements all the above http headers, client side and server side caching and even compression.

There is also a log module and an authorization module that should go a long way into how to implement authentication and logging.

Taliesin