Использование Handler для отслеживания файлов
В настоящее время я работаю над сайтом, который разрабатывается с использованием Asp.Net и С#. Я использую Asp.Net Handler
, чтобы пользователи могли загружать файлы. Я могу скачать файлы без проблем. Однако мне нужно регистрировать, какие файлы были загружены успешно. Кажется, эта часть работает неправильно. Например. если я нажму на файл для загрузки, а затем нажмите "Отмена" в подсказке браузера, мой код все еще записывается в журнал. Я не могу понять, как я могу записать в журнал только, когда файл успешно загружен.
Мой код ниже.
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();
}
Я ценю вашу помощь и поддержку.
Ответы
Ответ 1
Я попытался создать простой обработчик, который может обнаружить, что загрузка была отменена/нарушена с точки зрения сервера.
Важной частью является "context.Response.IsClientConnected" во время/после отправки данных.
В этом примере будет отправляться бесконечный файл со случайными данными. Вы можете протестировать его во всех браузерах, как именно они будут себя вести. Я тестировал его только в 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:
Отредактированный исходный код:
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();
}
Ответ 2
Я предполагаю, что кнопки загрузки и отмены находятся на одной странице.
Я создал следующий пример, чтобы проверить, загружен ли файл при отмене использования одного класса, который имеет флаг "isFileDownload" , чтобы значение флага не было инициализировано для каждого запроса для того же пользователя.
- Каждый раз, когда запрос поступает от пользователя, назначьте "isFileDownload" json
переменная в "isFileDownload" для одноэлементного класса.
- Когда запрос на загрузку файла приходит к обработчику, "isFileDownload" singleton будет установлен в true. при отправке ответа пользователю, проверьте, действительно ли "isFileDownload" true den log else dont.
- если пользователь нажимает кнопку загрузки, тогда "isFileDownload" = true, и пользователь нажимает кнопку отмены до того, как файл будет загружен, тогда "isFileDownload" = false.
- Так как свойство isFileDownload является частью класса singleton, тот же экземпляр будет обновлен.
FYI, Я новичок в обработчиках. Заранее спросите, если я пропустил что-то, чтобы принять во внимание при публикации этого
Класс обработчика:
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: Ссылка
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 Страница:
$(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>
Ответ 3
Когда появляется всплывающее окно браузера, это означает, что сервер начал отправлять (передавать) данные клиенту. В действительности вы не можете определить, была ли загрузка файла успешной или нет.
Вы можете только определить, сколько байтов было отправлено клиенту, и если количество байтов равно длине файла, вы можете предположить, что клиент смог загрузить файл, но вы не можете получить доказательство того, что файл был успешно сохранен на клиентская машина/среда.
Пример: если я загружаюсь под Linux и пересылаю поток в /dev/null, вы увидите, что я загрузил этот файл, но на самом деле файл не будет существовать на моей машине.