Как остановить передачу файлов, если браузер закрыт / загрузка отменена - PullRequest
4 голосов
/ 06 декабря 2011

Я загружаю файл асинхронно с HTML5 в MVC3. Если у меня большой файл, скажем, размером 1 ГБ, и после завершения загрузки на 50% я отменяю загрузку или закрываю браузер, он все равно сохраняет файл размером 500 МБ в целевой папке.

Как я могу решить эту проблему в контроллере и на стороне клиента?

Вот мое действие контроллера:

[HttpPost]
public ActionResult Upload(object fileToUpload1)
{
    var fileName = Request.Headers["X-File-Name"];
    var fileSize = Request.Headers["X-File-Size"];
    var fileType = Request.Headers["X-File-Type"];

    Request.SaveAs("D:\\uploadimage\\" + fileName, false);

    if (fileToUpload1 == null)
    {
        return Json(true, JsonRequestBehavior.AllowGet);
    }
    else { return Json(false, JsonRequestBehavior.AllowGet); }

    // return Json(false, JsonRequestBehavior.AllowGet);
}

А вот и Javascript:

// Uploading - for Firefox, Google Chrome and Safari
xhr = new XMLHttpRequest();

// Update progress bar
xhr.upload.addEventListener("progress", uploadProgress, false);

function uploadProgress(evt) {
    if (evt.lengthComputable) {
        var percentComplete = Math.round(evt.loaded * 100 / evt.total);    

        //assign value to prgress bar Div
        var progressBar = document.getElementById("progressBar");

        progressBar.max = evt.total;
        progressBar.value = evt.loaded;
    }
}

// File load event
xhr.upload.addEventListener("load", loadSuccess, false);

function loadSuccess(evt) { 
    $(fileParentDivobj).find(".ImgDiv").find("span").html("uploaded");
    addfile(fileParentDivobj);
}

//handling error
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);

function uploadFailed(evt) {
    alert("There was an error attempting to upload the file.");
}

function uploadCanceled(evt) {
    alert("The upload has been canceled by the user or the browser dropped the connection.");
}  

xhr.open("POST", "@Url.Action("Upload","Home")", true);

// Set appropriate headers
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Name", file.fileName);
xhr.setRequestHeader("X-File-Size", file.fileSize);
xhr.setRequestHeader("X-File-Type", file.type);
xhr.setRequestHeader("X-File", file);

// Send the file (doh)
xhr.send(file);

Ответы [ 2 ]

4 голосов
/ 11 декабря 2011

Во-первых, это не то, что должно быть решено с помощью каких-либо клиентских сценариев, так как я не верю, что вы сможете сделать новый запрос, когда браузер закрывается, и он, конечно, не будет работать, если соединение прервано из-за проблемы с сетью.

Итак, я немного покопался и не нашел в asp.net ничего такого, что сообщало бы, что соединение с запросом было прервано. Однако мы можем проверить, сколько данных мы получили и сколько данных мы должны были получить!

public ActionResult Upload()
{
    // I like to keep all application data in App_Data, feel free to change this
    var dir = Server.MapPath("~/App_Data");
    if (!Directory.Exists(dir))
        Directory.CreateDirectory(dir);

    // extract file name from request and make sure it doesn't contain anything harmful
    var name = Path.GetFileName(Request.Headers["X-File-Name"]);
    foreach (var c in Path.GetInvalidFileNameChars())
        name.Replace(c, '-');

    // construct file path
    var path = Path.Combine(dir, name);

    // this variable will hold how much data we have received
    var written = 0;
    try
    {
        using (var output = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            var buffer = new byte[0x1000];
            var read = 0;

            // while there is something to read, write it to output and increase counter
            while ((read = Request.InputStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, read);
                output.Flush();

                written += read;
            }
        }
    }
    finally
    {
        // once finished (or when exception was thrown) check whether we have all data from the request
        // and if not - delete the file
        if (Request.ContentLength != written)
            System.IO.File.Delete(path);
    }

    return Json(new { success = true });
}

Протестировано с вашим клиентским кодом с использованием сервера asp.net dev и google chrome.

edit: только что заметил, что Чак Сэвидж ранее опубликовал этот принцип в комментариях, так что подпишитесь на него:)

0 голосов
/ 12 декабря 2011

Сосредоточение внимания на запросе является причиной того, что вы забыли ответ, который может сказать, подключен ли клиент по-прежнему (Response.IsClientConnected).
Просто проверяя, есть ли что-то для чтения, вы игнорируете случай возможногодлительная (как долго?) сетевая задержка со стороны клиента.
Используйте подход Chunk и Lukas и включите свойство Response.IsClientConnected и поток Sleep по вашему выбору на случай, если нечего читать, но клиент все еще подключен.Таким образом, при необходимости вы выйдете из цикла чтения раньше, не создавая WTF от пользователя клиента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...