Следуя нескольким учебным пособиям и примерам, я построил систему загрузки файлов, основанную на 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/
У меня такое ощущение, что ответ сводится к "Почему это работает в блоге Дэвида Уолша, а не на скрипке?"