I'm currently working on a website which is being developed using Asp.Net and C#. I'm making use of Asp.Net Handler
to allow users to download files. I can download the files no problem. However I need to log which files were downloaded successfully. This part doesn't seem to work correctly for me. E.g. if I click on the file to download and then click cancel on the browser prompt my code still writes to the log. I can't seem to figure out how can I write to log only when the file has successfully downloaded.
My Code is below.
public void ProcessRequest(HttpContext context)
{
string logFilePath = "PathToMyLogFile";
string filePath = Uri.UnescapeDataString(context.Request.QueryString["file"]);
string fileName = Path.GetFileName(filePath);
if (context.Response.IsClientConnected) //Shouldn't this tell me if the client is connected or not?
{
using (var writer = new StreamWriter(logFilePath, true))
{
if (!File.Exists(logFilePath))
{
//Create log file if one does not exist
File.Create(logFilePath);
}
else
{
writer.WriteLine("The following file was downloaded \"{0}\" on {1}", fileName, DateTime.Now.ToString("dd/MM/yyyy") + " at " + DateTime.Now.ToString("HH:mm:ss"));
writer.WriteLine(Environment.NewLine + "-----------------------------------------------------------------------------" + Environment.NewLine);
}
}
}
context.Response.ContentType = "application/octet-stream";
context.Response.AppendHeader("Content-Disposition", "attachment;filename=\"" + Path.GetFileName(filePath));
context.Response.WriteFile(filePath);
context.Response.End();
}
I appreciate all your help and support.
I tried create simple handler which can detect the download was canceled/broken from server-side perspective.
The important part is "context.Response.IsClientConnected" during/after the sending data.
This example will send neverending file with random data. You can test it in all browser, how exactly they will behave. I tested it only in Chrome.
/// <summary>
/// Writes random data.
/// </summary>
public class NeverendingFile : IHttpHandler {
public bool IsReusable {
get { return false; }
}
public void ProcessRequest(HttpContext context) {
context.Response.Buffer = false;
context.Response.BufferOutput = false;
context.Response.ContentType = "application/octet-stream";
context.Response.AppendHeader("Content-Disposition", "attachment;filename=\"Neverendingfile.dat\"");
context.Response.Flush();
// flag used for debuging, in production it will be always false => writing into output stream will nevere ends
var shouldStop = false;
for(var i = 0; !shouldStop; i++) {
// chunk contains random data
var chunk = Guid.NewGuid().ToByteArray();
for (var a = 0; a < 1000; a++) {
context.Response.OutputStream.Write(chunk, 0, chunk.Length);
}
context.Response.OutputStream.Flush();
// sleep is just for slowing the download
System.Threading.Thread.Sleep(10);
if (!context.Response.IsClientConnected) {
// the download was canceled or broken
return;
}
}
}
}
EDIT: Edited source code:
public void ProcessRequest(HttpContext context) {
string logFilePath = "PathToLogFile";
//Determine the file path
string filePath = Uri.UnescapeDataString(context.Request.QueryString["file"]);
//Determine the file name
string fileName = Path.GetFileName(filePath);
context.Response.Buffer = false;
context.Response.BufferOutput = false;
context.Response.ContentType = "application/octet-stream";
context.Response.AppendHeader("Content-Disposition", "attachment;filename=\"" + Path.GetFileName(filePath));
context.Response.WriteFile(filePath);
context.Response.Flush();
context.Response.OutputStream.Flush();
if (!context.Response.IsClientConnected) {
// the download was canceled or broken
using (var writer = new StreamWriter(logFilePath, true)) {
if (!File.Exists(logFilePath)) {
//Create log file if one does not exist
File.Create(logFilePath);
}
else {
writer.WriteLine("The Download was canceled");
}
}
return;
}
context.Response.End();
}
I am assuming that both download and cancel buttons are on same page. I created the following example to check if the file is already started download while canceling using a singleton class that have a flag "isFileDownload" so that the flag value wont be initialized for every request for the same user.
FYI, i am new to handlers.Sorry in advance if I missed something to take into account while posting this
Handler Class:
public class DownloadFile : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string fileName = @"test.txt";
string filePath = context.Server.MapPath("/test.txt");
string logFilePath = context.Server.MapPath("/Log.txt");
//string filePath = Uri.UnescapeDataString(context.Request.QueryString["file"]);
//string fileName = Path.GetFileName(filePath);
Singleton s = Singleton.Instance;
s.isFileDownload = Convert.ToBoolean(context.Request.Form["isFileDownload"]);
if (context.Response.IsClientConnected) //Shouldn't this tell me if the client is connected or not?
{
using (var writer = new StreamWriter(logFilePath,true))
{
if (!File.Exists(logFilePath))
{
//Create log file if one does not exist
File.Create(logFilePath);
}
else
{
writer.WriteLine("The following file was downloaded \"{0}\" on {1}", fileName, DateTime.Now.ToString("dd/MM/yyyy") + " at " + DateTime.Now.ToString("HH:mm:ss"));
writer.WriteLine(Environment.NewLine + "-----------------------------------------------------------------------------" + Environment.NewLine);
}
}
}
//To mock the large file download
if (s.isFileDownload)
System.Threading.Thread.Sleep(10000);
if (context.Response.IsClientConnected )
{
if (s.isFileDownload){
System.Threading.Thread.Sleep(100);
context.Response.ContentType = "application/octet-stream";
context.Response.AppendHeader("Content-Disposition", "attachment;filename=\"" + Path.GetFileName(filePath));
context.Response.WriteFile(filePath);
context.Response.OutputStream.Flush();
context.Response.End();
}
else
{
return;
}
}
else
{
return;
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
Singleton class: Reference
public sealed class Singleton
{
private static volatile Singleton instance;
private static object syncRoot = new Object();
private Singleton() { }
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
public bool isFileDownload { get; set; }
}
Html Page :
$(document).ready(function(){
var isFileDownload = false;
$("#download").click(function () {
isFileDownload = true;
console.log(isFileDownload);
$.ajax({
method: "POST",
url: "DownloadFile.ashx",
data: { isFileDownload:true }
}).done(function (data) {
alert(data);
});
});
$("#cancel").click(function () {
if (isFileDownload) {
$.ajax({
method: "POST",
url: "DownloadFile.ashx",
data: { isFileDownload: false }
}).done(function (data) {
alert("canceld");
isFileDownload = false;
});
}
});
$(document).ajaxComplete(function () {
isFileDownload = false;
});
)};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<button type="button" value="DownLoad" id="download" title="Download">Download</button>
<button type="button" value="Cancel" id="cancel" title="Cancel">Cancel</button>
</body>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With