Скачатьигнорирует атрибут загрузки - PullRequest
0 голосов
/ 25 октября 2018

Следуя нескольким учебным пособиям и примерам, я построил систему загрузки файлов, основанную на jQuery на клиенте и MS WebAPI на сервере.

Обеспечение прямых ссылок на файлы невозможно, так какAPI требует аутентификации, поэтому URL-адрес файла является конечной точкой API, а не местоположением файла.

На сервере у меня есть это:

[HttpGet]
[Route("download/{filename}")]
public HttpResponseMessage DownloadFile(string filename)
{
    try
    {
        // https://gist.github.com/joeriks/3714093
        string path = string.Format("{0}/Exports/{1}", root, filename);

        HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
        var stream = new FileStream(path, FileMode.Open);
        result.Content = new StreamContent(stream);
        result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
        result.Content.Headers.ContentDisposition.FileName = "download.txt";
        return result;

    }
    catch (Exception ex)
    {
        throw new HttpResponseException(HttpStatusCode.InternalServerError);
    }
}

, который возвращает ожидаемый ответ:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 4809
Content-Type: application/octet-stream
Expires: -1
Server: Microsoft-IIS/10.0
Content-Disposition: attachment; filename=download.txt
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, PUT, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: content-Type, accept, origin, X-Requested-With, X-Authentication, X-Nonce, name
Date: Thu, 25 Oct 2018 13:07:25 GMT

С текстовым содержанием в ответе.Пока все так, как я надеюсь.

На клиенте у меня есть следующее для обработки ответов от моего API:

// https://stackoverflow.com/a/23797348
let disposition = jqXHR.getResponseHeader('Content-Disposition');

if (disposition && disposition.indexOf('attachment') !== -1) {
    let filename = "scada-download.txt";
    let matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
    if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');

    let type = jqXHR.getResponseHeader('Content-Type');
    let blob = new Blob([data], { type: "text/csv" });

    var downloadUrl = URL.createObjectURL(blob);
    let $a = $("<a id='temp_download_link' style='display: none;' />").attr("href", downloadUrl).attr("download", filename);
    $("body").append($a);
    $a.trigger("click");
}

Это делается так, как объявлено, и добавляет привязку к страницеи щелкает по нему, вызывая загрузку.

Загрузка работает и сохраняет файл с правильным содержимым.

Единственное, что не работает, это то, что в обоих браузерах, которые я тестировалв (Chrome 69, FF: 62) имя файла по умолчанию - просто GUID.

API и клиентский код в настоящее время выполняются с моей локальной машины разработчика, http://127.0.0.1:9000/[client | api], поэтому перекрестное происхождение не должноне будет играть никакой роли.

Вызов API выполняется через AJAX.В конечном счете, метод jQuery $ .ajax ()

Для ясности, якорь, вставленный в DOM:

<a id="temp_download_link" style="display: none;" href="blob:http://127.0.0.1:9000/c2c5ffb5-3f22-4a57-8775-4e0bbfbfef9e" download="download.txt"></a>

Имя файла по умолчанию, предоставляемое Chrome, - это GUID в URL, FFгенерирует, казалось бы, неподключенную случайную строку из 6 символов.

В частности, почему браузеры игнорируют атрибут download="download.txt" привязки и Content-Disposition: attachment; filename=download.txt?

ОБНОВЛЕНИЕ:

Я разветвлял эту скрипку:

http://jsfiddle.net/Qjvb3/

И добавил некоторые другие значения для атрибута href, похоже, что вся настройка имени файла является нестабильнойв лучшем случае:

http://jsfiddle.net/yubjqwvs/

ОБНОВЛЕНИЕ 2

Я скопировал ссылку из рабочего примера в мою скрипку, она работает изоригинальный сайт, но не из скрипки.

http://jsfiddle.net/yubjqwvs/2/

У меня такое ощущение, что ответ сводится к "Почему это работает в блоге Дэвида Уолша, а не на скрипке?"

Ответы [ 3 ]

0 голосов
/ 13 ноября 2018

Причина, которую я вижу, заключается в том, что вы используете функцию jQuery .attr () вместо функции .prop () .Существует разница между свойствами и атрибутами HTML5.Первый дает разметку для элементов в случаях для привязки событий и т. Д., А другой дает доступ к / устанавливает значение самого элемента DOM.

Я привожу объяснение, которое может быть полезно здесь .

0 голосов
/ 26 февраля 2019

В конце концов я вернулся к этой проблеме, когда стало критически важно ее исправить.Оказалось, что моя (доморощенная) структура SPA была проблемой, поскольку она переопределяла события щелчка по умолчанию в браузере <a />

Я исправил это, добавив улов для ссылок, которые являются BLOB-объектами, а затем запустив по умолчаниюсобытие клика:

// convert all a/href to a#href
$("body").delegate("a", "click", function () {
    let href: string = $(this).attr("href"); 

    // check its not an external / absolute URL
    let regex: RegExp = new RegExp("^(blob:)?(http|https)(:\/\/)", "ig");
    let match = regex.exec(href);
    if (match) {
        // match[0] is the full match, match[1] is lookign for "blob:"
        // it will either be undefined or blob:
        if (match[1]) { // its a blob url, call the default
            return true; // !! this line is the core of the fix !!
        }
        else { // load a normal link
            // see if its got a target.
            let target: string = $(this).attr("target");

            switch (target) {
                default: document.location.href = href; break;
                case "_blank": window.open(href); break;
            }
        }   
    }
    else
        // SPA stuff
    return false;
});

Так что, по сути, это проблема, с которой никто никогда бы не столкнулся, если только они, как я, не настолько глупы, чтобы играть свою собственную структуру SPA.

Это здесь https://github.com/JohnRayson/JSPA

0 голосов
/ 13 ноября 2018

Я лично использовал downloadjs , чтобы избежать подобных проблем в моем случае:

  • Для загрузки файла сделан запрос AJAX;это просто PHP-скрипт с соответствующим заголовком (Content-Disposition: attachment; filename="...")
  • Пользователь щелкает ссылку, которая использует событие hashchange для запуска загрузки, после которой я передаю ответ AJAX в downloadjs.Я полагаю, что то же самое можно сделать, используя URL-адрес BLOB-объекта.

Код downloadjs очень похож на jsfiddle , в зависимости от вашего примера.Он отлично работает с Firefox 63, Opera 56 (полагаю, он должен работать в Chrome) и не работает в IE 11, но это IE.

  • downloadjs создает скрытую ссылку с таймаутом, установленным в66 мс (я полагаю, что это ожидание готовности DOM или чего-то в этом роде).
  • Тайм-аут вызывает click() по ссылке, которая, вероятно, вызывает собственный обработчик.

С вашим кодом и использованием click(), а также document.getElementById() вместо jQuery, дайте нам это jsfiddle .В отличие от downloadjs, я не использую window.setTimeout, и он отлично работает, по крайней мере, в Fx 63. Это может быть просто способ обойти ошибку браузера.

В вашем случае, я думаю, чтоjQuery не запускает обработчик по умолчанию, когда вы вызываете trigger('click'), который я не понимаю, так как документация trigger говорит нам, что это так:

Начиная с jQuery 1.3, события .trigger () edвсплыть дерево DOM;обработчик события может остановить всплывание, возвращая false из обработчика или вызывая метод .stopPropagation () для объекта события, переданного в событие.Хотя .trigger () имитирует активацию события, в комплекте с синтезированным объектом события, она не полностью воспроизводит естественное событие.

Для запуска обработчиков, связанных с помощью jQuery, без запуска собственного события, используйте .triggerHandler.() вместо.(цитата из документации jquery )

Возможно, что щелчок по умолчанию по ссылкам не считается обработчиком (?) для jquery и не запускается.

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