Обработка загрузки файла из поста ajax


393

У меня есть приложение javascript, которое отправляет запросы POST ajax на определенный URL. Ответ может быть строкой JSON или файлом (в виде вложения). Я легко могу определить Content-Type и Content-Disposition в своем вызове ajax, но как только я обнаружу, что ответ содержит файл, как я могу предложить клиенту загрузить его? Я прочитал несколько похожих тем здесь, но ни одна из них не дает ответ, который я ищу.

Пожалуйста, пожалуйста, пожалуйста, не публикуйте ответы, предлагающие мне не использовать ajax для этого или что я должен перенаправить браузер, потому что ничего из этого не вариант. Использование простой HTML-формы также не вариант. Мне нужно показать клиенту диалог загрузки. Можно ли это сделать и как?


Для тех, кто читает эту статью, прочитайте этот пост: stackoverflow.com/questions/20830309/…
sobhan

Я убрал ваше решение из вопроса. Вы можете опубликовать его как ответный пост ниже, но он не относится к посту с вопросом.
Мартин Питерс

Ответы:


111

Создайте форму, используйте метод POST, отправьте форму - нет необходимости в iframe. Когда страница сервера отвечает на запрос, напишите заголовок ответа для mime-типа файла, и он откроет диалоговое окно загрузки - я делал это несколько раз.

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


35
Как указано в вопросе: «Использование простой HTML-формы также не вариант».
Павле Предич

13
Нет, потому что использование обычного POST приведет к переходу браузера к URL-адресу POST. Я не хочу уходить со страницы. Я хочу выполнить запрос в фоновом режиме, обработать ответ и представить его клиенту.
Павле Предич

6
Если сервер отправляет назад заголовки, как и другой ответ, он открывается в новом окне - я делал это раньше. Он ушел бы, только если ваш серверный скрипт вернул HTML-код

1
@PavlePredic Вы в конечном итоге выяснили, как управлять обоими сценариями ответов, то есть текстовым ответом JSON или ответом загрузки файла?
Веб-пользователь

9
Ответ не ясен, и предлагаемое решение не работает.
stack247

531

Не сдавайтесь так быстро, потому что это можно сделать (в современных браузерах), используя части FileAPI:

var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
    if (this.status === 200) {
        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        }
        var type = xhr.getResponseHeader('Content-Type');

        var blob;
        if (typeof File === 'function') {
            try {
                blob = new File([this.response], filename, { type: type });
            } catch (e) { /* Edge */ }
        }
        if (typeof blob === 'undefined') {
            blob = new Blob([this.response], { type: type });
        }

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') {
                    window.location.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send($.param(params));

Вот старая версия с использованием jQuery.ajax. Он может искажать двоичные данные, когда ответ преобразуется в строку некоторой кодировки.

$.ajax({
    type: "POST",
    url: url,
    data: params,
    success: function(response, status, xhr) {
        // check for a filename
        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        }

        var type = xhr.getResponseHeader('Content-Type');
        var blob = new Blob([response], { type: type });

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') {
                    window.location.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
});

1
Большое спасибо ! Мне нужно было добавить «Content-disposition» в «Access-Control-Expose-Headers» и «Access-Control-Allow-Headers» в моем HTTP-ответе, чтобы все заработало.
JulienD

1
Это не работает, когда файл больше, чем 500 МБ, может быть, мы должны использовать другой API?
Хирра

Как насчет удаления элемента из DOM в части очистки (а не только в URL)? document.body.removeChild(a);
Scoregraphic

@hirra использует responseceType «blob» вместо «arraybuffer» и переписывает функцию обратного вызова onload таким образом, что var blob - это this.response (var blob = this.response;) такvar blob =this.responce; /** if (typeof File === 'function') { try { blob = new File([this.response], filename, { type: type }); } catch (e) { /* Edge */ } } if (typeof blob === 'undefined') { blob = new Blob([this.response], { type: type }); } */
Крис Тобба,

1
Это идеальное решение. Только одно небольшое изменение. В Typescript мне нужно было window.location.href = downloadUrlвместоwindow.location = downloadUrl
michal.jakubeczy

39

Я столкнулся с той же проблемой и успешно решил ее. Мой вариант использования это.

« Отправьте данные JSON на сервер и получите файл Excel. Этот файл Excel создается сервером и возвращается как ответ клиенту. Загрузите этот ответ как файл с пользовательским именем в браузере »

$("#my-button").on("click", function(){

// Data to post
data = {
    ids: [1, 2, 3, 4, 5]
};

// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    var a;
    if (xhttp.readyState === 4 && xhttp.status === 200) {
        // Trick for making downloadable link
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        // Give filename you wish to download
        a.download = "test-file.xls";
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
    }
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});

Вышеуказанный фрагмент просто делает следующее

  • Отправка массива в виде JSON на сервер с использованием XMLHttpRequest.
  • После извлечения содержимого в виде двоичного объекта (двоичного объекта) мы создаем загружаемый URL-адрес и прикрепляем его к невидимой ссылке «а», а затем нажимаем на нее.

Здесь нам нужно тщательно настроить несколько вещей на стороне сервера. Я установил несколько заголовков в Python Django HttpResponse. Вам необходимо установить их соответствующим образом, если вы используете другие языки программирования.

# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

Так как я загружаю xls (excel) здесь, я настроил contentType выше одного. Вам нужно установить его в соответствии с вашим типом файла. Вы можете использовать эту технику для загрузки любых файлов.


33

Какой серверный язык вы используете? В моем приложении я могу легко загрузить файл из вызова AJAX, установив правильные заголовки в ответе PHP:

Установка заголовков на стороне сервера

header("HTTP/1.1 200 OK");
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

// The optional second 'replace' parameter indicates whether the header
// should replace a previous similar header, or add a second header of
// the same type. By default it will replace, but if you pass in FALSE
// as the second argument you can force multiple headers of the same type.
header("Cache-Control: private", false);

header("Content-type: " . $mimeType);

// $strFileName is, of course, the filename of the file being downloaded. 
// This won't have to be the same name as the actual file.
header("Content-Disposition: attachment; filename=\"{$strFileName}\""); 

header("Content-Transfer-Encoding: binary");
header("Content-Length: " . mb_strlen($strFile));

// $strFile is a binary representation of the file that is being downloaded.
echo $strFile;

Это фактически «перенаправит» браузер на эту страницу загрузки, но, как сказал @ahren alread в своем комментарии, он не уйдет с текущей страницы.

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

Обработка ответной стороны клиента

Предполагая, что вы уже знаете, как сделать вызов AJAX, на стороне клиента вы выполняете запрос AJAX к серверу. Затем сервер генерирует ссылку, откуда этот файл может быть загружен, например, «прямой» URL, на который вы хотите указать. Например, сервер отвечает:

{
    status: 1, // ok
    // unique one-time download token, not required of course
    message: 'http://yourwebsite.com/getdownload/ska08912dsa'
}

При обработке ответа вы вставляете iframeв свое тело и устанавливаете для iframeSRC URL-адрес, который вы только что получили, вот так (используя jQuery для простоты этого примера):

$("body").append("<iframe src='" + data.message +
  "' style='display: none;' ></iframe>");

Если вы установили правильные заголовки, как показано выше, iframe вызовет диалог загрузки без перехода браузера от текущей страницы.

Запись

Дополнительное дополнение по вашему вопросу; Я думаю, что лучше всегда возвращать JSON при запросе материала с технологией AJAX. После получения ответа JSON вы можете решить, что с ним делать на стороне клиента. Может быть, например, позже вы захотите, чтобы пользователь щелкнул ссылку для загрузки на URL-адрес, а не заставлял загрузку напрямую, в вашей текущей настройке вам придется обновить как клиентскую, так и серверную сторону, чтобы сделать это.


24

Для тех, кто ищет решение с угловой точки зрения, это сработало для меня:

$http.post(
  'url',
  {},
  {responseType: 'arraybuffer'}
).then(function (response) {
  var headers = response.headers();
  var blob = new Blob([response.data],{type:headers['content-type']});
  var link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = "Filename";
  link.click();
});

Это помогло, но мне нужно сохранить оригинальное имя файла. Я вижу имя файла в заголовках ответа в разделе «Content-Disposition», но не могу найти это значение в объекте ответа в коде. Установка link.download = ""приводит к случайному имени файла guid, и link.download = nullприводит к файлу с именем "null".
Мари

@Marie, вы можете записать имя файла во время загрузки, используя свойство INPUTэлемента HTMLInputElement.files. См . Документацию MDN для ввода файла для более подробной информации.
Тим Хеттлер

Размер
BLOB-объекта

22

Вот как у меня это работает https://stackoverflow.com/a/27563953/2845977

$.ajax({
  url: '<URL_TO_FILE>',
  success: function(data) {
    var blob=new Blob([data]);
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="<FILENAME_TO_SAVE_WITH_EXTENSION>";
    link.click();
  }
});

Обновленный ответ с использованием download.js

$.ajax({
  url: '<URL_TO_FILE>',
  success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
});


Спасибо за это, я только что использовал это сегодня. Фантастика
Райан Уилсон

Привет, мне нужно иметь jQuery 3.0>, чтобы это работало?
gbade_

Я также получаю пустой PDF с обоими примерами, которые вы привели. Я пытаюсь получить его для загрузки файла PDF.
gbade_

@gbade_ Нет, вам не нужна какая-либо конкретная версия jQuery. Должно работать нормально со всеми версиями. Вы проверяли, имеют ли загружаемый PDF-файл правильные заголовки CORS? Любые ошибки на консоли могут помочь при отладке
Mayur Padshala

Это работало для меня, используя download.js:success: function (response, status, request) { download(response, "filename.txt", "application/text"); }
Sga

12

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

У меня была такая же проблема пару недель назад, действительно, невозможно добиться "чистой" загрузки через AJAX, Filament Group создала плагин jQuery, который работает точно так, как вы уже узнали, он называется jQuery File Скачать, однако, есть и обратная сторона этой техники.

Если вы отправляете большие запросы через AJAX (скажем, файлы + 1 МБ), это отрицательно скажется на скорости отклика. В медленных подключениях к Интернету , вам придется ждать много , пока запрос не посылаются , а также ждать файл для загрузки. Это не похоже на мгновенный щелчок "=" "всплывающее окно" => "начало загрузки". Это больше похоже на «щелчок» => «ожидание отправки данных» => «ожидание ответа» => «запуск загрузки», при котором файл выглядит в два раза больше, потому что вам придется ждать отправки запроса через AJAX и вернуть его в виде загружаемого файла.

Если вы работаете с файлами небольшого размера <1 МБ, вы этого не заметите. Но, как я обнаружил в своем собственном приложении, для файлов большего размера это практически невыносимо.

Мое приложение позволяет пользователям экспортировать изображения, сгенерированные динамически, эти изображения отправляются через POST-запросы в формате base64 на сервер (это единственный возможный способ), затем обрабатываются и отправляются пользователям в виде файлов .png, .jpg, base64. Строки для изображений + 1 МБ огромны, это заставляет пользователей ждать больше, чем необходимо, чтобы файл начал загружаться. В медленных интернет-соединениях это может быть действительно раздражающим.

Мое решение для этого состояло в том, чтобы временно записать файл на сервер, когда он будет готов, динамически сгенерировать ссылку на файл в виде кнопки, которая изменяется между состояниями «Пожалуйста, подождите ...» и «Загрузить» и одновременно распечатайте изображение base64 во всплывающем окне предварительного просмотра, чтобы пользователи могли «щелкнуть правой кнопкой мыши» и сохранить его. Это делает все время ожидания более терпимым для пользователей, а также ускоряет процесс.

Обновление 30 сентября 2014 г .:

С тех пор, как я это опубликовал, прошли месяцы, и, наконец, я нашел лучший подход для ускорения работы с большими строками base64. Теперь я сохраняю строки base64 в базе данных (используя поля longtext или longblog), затем я передаю его идентификатор записи через jQuery File Download, наконец, в файле сценария загрузки, я запрашиваю базу данных, используя этот идентификатор, чтобы извлечь строку base64 и передать ее через функция загрузки.

Пример загрузки скрипта:

<?php
// Record ID
$downloadID = (int)$_POST['id'];
// Query Data (this example uses CodeIgniter)
$data       = $CI->MyQueries->GetDownload( $downloadID );
// base64 tags are replaced by [removed], so we strip them out
$base64     = base64_decode( preg_replace('#\[removed\]#', '', $data[0]->image) );
// This example is for base64 images
$imgsize    = getimagesize( $base64 );
// Set content headers
header('Content-Disposition: attachment; filename="my-file.png"');
header('Content-type: '.$imgsize['mime']);
// Force download
echo $base64;
?>

Я знаю, что это далеко от того, о чем просил ФП, однако я чувствовал, что было бы неплохо дополнить мой ответ своими выводами. Когда я искал решения для своей проблемы, я прочитал множество веток «Загрузка из данных AJAX POST», которые не дали мне ответ, который я искал, я надеюсь, что эта информация поможет кому-то, стремящемуся достичь чего-то подобного.


jQuery File DownloadТолько перенаправить меня к ссылке. Я называю это так: jQuery.download("api/ide/download-this-file.php", {filePath: path2Down}, "POST");.
Каспер

5

Я хочу указать на некоторые трудности, которые возникают при использовании техники в принятом ответе, то есть при использовании формы сообщения:

  1. Вы не можете установить заголовки по запросу. Если ваша схема аутентификации включает заголовки, Json-Web-Token, переданный в заголовке Authorization, вам нужно будет найти другой способ отправить его, например, в качестве параметра запроса.

  2. Вы не можете точно сказать, когда запрос закончился. Что ж, вы можете использовать cookie, который устанавливается в ответ, как это делает jquery.fileDownload , но это FAR от идеального. Он не будет работать для одновременных запросов и сломается, если ответ так и не поступит.

  3. Если сервер отвечает с ошибкой, пользователь будет перенаправлен на страницу ошибки.

  4. Вы можете использовать только типы контента, поддерживаемые формой . Это означает, что вы не можете использовать JSON.

В итоге я использовал метод сохранения файла на S3 и отправки предварительно подписанного URL-адреса, чтобы получить файл.


5

Для тех, кто ищет более современный подход, вы можете использовать fetch API. В следующем примере показано, как загрузить файл электронной таблицы. Это легко сделать с помощью следующего кода.

fetch(url, {
    body: JSON.stringify(data),
    method: 'POST',
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    },
})
.then(response => response.blob())
.then(response => {
    const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
    const downloadUrl = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = downloadUrl;
    a.download = "file.xlsx";
    document.body.appendChild(a);
    a.click();
})

Я считаю, что этот подход гораздо легче понять, чем другие XMLHttpRequestрешения. Кроме того, он имеет синтаксис, аналогичныйjQuery подходу, без необходимости добавления каких-либо дополнительных библиотек.

Конечно, я бы посоветовал проверить, для какого браузера вы разрабатываете, так как этот новый подход не будет работать в IE. Вы можете найти полный список совместимых браузеров по следующей [ссылке] [1].

Важное замечание : В этом примере я отправляю запрос JSON серверу, который прослушивает данные url. Это urlдолжно быть установлено, на моем примере я предполагаю, что вы знаете эту часть. Также рассмотрите заголовки, необходимые для работы вашего запроса. Поскольку я отправляю JSON, я должен добавить Content-Typeзаголовок и установить его application/json; charset=utf-8, чтобы сервер знал тип запроса, который он получит.


1
Потрясающие! Чтобы открыть это в новой вкладке вместо всплывающего окна загрузки: используйте `` `const window = open (downloadUrl," _blank "); if (window! == null) window.focus (); `` `
Энди

Есть ли способ сделать это для нескольких наборов данных? Например, получить обратно {fileOne: data, fileTwo: data, fileThree: data} из вызова API и сгенерировать три загруженных файла одновременно? Спасибо!
il0v3d0g

Хм, я не пробовал это. Но вы всегда можете сжать изображения в ZIP-файл и скачать его. Я проверю, если это возможно.
Ален Круз

4

Вот мое решение с использованием временной скрытой формы.

//Create an hidden form
var form = $('<form>', {'method': 'POST', 'action': this.href}).hide();

//Add params
var params = { ...your params... };
$.each(params, function (k, v) {
    form.append($('<input>', {'type': 'hidden', 'name': k, 'value': v}));
});

//Make it part of the document and submit
$('body').append(form);
form.submit();

//Clean up
form.remove();

Обратите внимание, что я широко использую JQuery, но вы можете сделать то же самое с нативным JS.


3

Как уже говорили другие, вы можете создать и отправить форму для загрузки через запрос POST. Однако вам не нужно делать это вручную.

Одна действительно простая библиотека для выполнения именно этого - jquery.redirect . Он предоставляет API, похожий на стандартный jQuery.postметод:

$.redirect(url, [values, [method, [target]]])

3

Это вопрос на 3 года, но у меня была такая же проблема сегодня. Я посмотрел ваше отредактированное решение, но думаю, что оно может принести в жертву производительность, потому что оно должно сделать двойной запрос. Так что если кому-то нужно другое решение, которое не подразумевает повторного вызова службы, то я так и сделал:

<form id="export-csv-form" method="POST" action="/the/path/to/file">
    <input type="hidden" name="anyValueToPassTheServer" value="">
</form>

Эта форма просто используется для вызова службы и избегает использования window.location (). После этого вам просто нужно отправить форму из jquery, чтобы вызвать сервис и получить файл. Это довольно просто, но таким образом вы можете сделать загрузку с помощью POST . Теперь я понимаю, что это может быть проще, если услуга, которую вы вызываете, является GET , но это не мой случай.


1
Это не сообщение ajax, так как вопрос использует ajax
Nidhin David

это просто решение вышеуказанной проблемы, но не для вызовов ajax.
Номи Али

1

Я использовал этот FileSaver.js . В моем случае с CSV-файлами я сделал это (в coffescript):

  $.ajax
    url: "url-to-server"
    data: "data-to-send"
    success: (csvData)->
      blob = new Blob([csvData], { type: 'text/csv' })
      saveAs(blob, "filename.csv")

Я думаю, что для самого сложного случая данные должны быть обработаны должным образом. Под капотом FileSaver.js реализован тот же подход, что и в ответе Джонатана Аменда .


1
..но вы можете скачать файлы обычно в iOS?
Алекс Маршалл

1

см .: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/ он вернет в качестве ответа большой двоичный объект, который затем может быть помещен в файловую заставку


2
Хотя это может теоретически ответить на вопрос, было бы предпочтительным включить здесь основные части ответа и предоставить ссылку для справки.
Бхаргав Рао

1

Чтобы получить ответ Джонатана Аменса на работу в Edge, я внес следующие изменения:

var blob = typeof File === 'function'
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

к этому

var f = typeof File+"";
var blob = f === 'function' && Modernizr.fileapi
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

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


0

Вот мое решение, собранное из разных источников: Реализация на стороне сервера:

    String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
    // Set headers
    response.setHeader("content-disposition", "attachment; filename =" + fileName);
    response.setContentType(contentType);
    // Copy file to output stream
    ServletOutputStream servletOutputStream = response.getOutputStream();
    try (InputStream inputStream = new FileInputStream(file)) {
        IOUtils.copy(inputStream, servletOutputStream);
    } finally {
        servletOutputStream.flush();
        Utils.closeQuitely(servletOutputStream);
        fileToDownload = null;
    }

Реализация на стороне клиента (с использованием jquery):

$.ajax({
type: 'POST',
contentType: 'application/json',
    url: <download file url>,
    data: JSON.stringify(postObject),
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert(errorThrown);
    },
    success: function(message, textStatus, response) {
       var header = response.getResponseHeader('Content-Disposition');
       var fileName = header.split("=")[1];
       var blob = new Blob([message]);
       var link = document.createElement('a');
       link.href = window.URL.createObjectURL(blob);
       link.download = fileName;
       link.click();
    }
});   

0

есть другое решение для загрузки веб-страницы в ajax. Но я имею в виду страницу, которая должна быть сначала обработана, а затем загружена.

Для начала нужно отделить обработку страницы от загрузки результатов.

1) В вызове ajax выполняются только расчеты страницы.

$ .post ("CalculusPage.php", {calculusFunction: true, ID: 29, данные1: "a", данные2: "b"},

       функция (данные, статус) 
       {
            если (статус == "успех") 
            {
                / * 2) В ответ загружается страница, которая использует предыдущие расчеты. Например, это может быть страница, которая печатает результаты таблицы, вычисленной в вызове ajax. * /
                window.location.href = DownloadPage.php + "? ID =" + 29;
            }               
       }
);

// Например: в CalculusPage.php

    if (! empty ($ _ POST ["calculusFunction"])) 
    {
        $ ID = $ _POST ["ID"];

        $ query = "INSERT INTO ExamplePage (data1, data2) VALUES ('". $ _ POST ["data1"]. "', '". $ _ POST ["data2"]. "') WHERE id =". $ ID;
        ...
    }

// Например: в DownloadPage.php

    $ ID = $ _GET ["ID"];

    $ sede = "SELECT * FROM ExamplePage WHERE id =". $ ID;
    ...

    $ Имя_файла = "Export_Data.xls";
    заголовок («Content-Type: application / vnd.ms-excel»);
    заголовок («Content-Disposition: inline; filename = $ filename»);

    ...

Я надеюсь, что это решение может быть полезным для многих, как и для меня.


0

Если ответ является буфером массива , попробуйте это в событии onsuccess в Ajax:

 if (event.data instanceof ArrayBuffer) {
          var binary = '';
          var bytes = new Uint8Array(event.data);
          for (var i = 0; i < bytes.byteLength; i++) {
              binary += String.fromCharCode(bytes[i])
          }
          $("#some_id").append("<li><img src=\"data:image/png;base64," + window.btoa(binary) + "\"/></span></li>");
          return;
      }
  • где event.data - ответ, полученный в функции успеха события xhr.

0

Ниже мое решение для загрузки нескольких файлов в зависимости от некоторого списка, который состоит из нескольких идентификаторов и поиска в базе данных, файлы будут определены и готовы к загрузке - если таковые существуют. Я вызываю действие C # MVC для каждого файла, используя Ajax.

И да, как говорили другие, это можно сделать в jQuery Ajax. Я сделал это с успехом Ajax, и я всегда посылаю ответ 200.

Итак, это ключ:

  success: function (data, textStatus, xhr) {

И это мой код:

var i = 0;
var max = 0;
function DownloadMultipleFiles() {
            if ($(".dataTables_scrollBody>tr.selected").length > 0) {
                var list = [];
                showPreloader();
                $(".dataTables_scrollBody>tr.selected").each(function (e) {
                    var element = $(this);
                    var orderid = element.data("orderid");
                    var iscustom = element.data("iscustom");
                    var orderlineid = element.data("orderlineid");
                    var folderPath = "";
                    var fileName = "";

                    list.push({ orderId: orderid, isCustomOrderLine: iscustom, orderLineId: orderlineid, folderPath: folderPath, fileName: fileName });
                });
                i = 0;
                max = list.length;
                DownloadFile(list);
            }
        }

Тогда звоню:

function DownloadFile(list) {
        $.ajax({
            url: '@Url.Action("OpenFile","OrderLines")',
            type: "post",
            data: list[i],
            xhrFields: {
                responseType: 'blob'
            },
            beforeSend: function (xhr) {
                xhr.setRequestHeader("RequestVerificationToken",
                    $('input:hidden[name="__RequestVerificationToken"]').val());

            },
            success: function (data, textStatus, xhr) {
                // check for a filename
                var filename = "";
                var disposition = xhr.getResponseHeader('Content-Disposition');
                if (disposition && disposition.indexOf('attachment') !== -1) {
                    var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                    var matches = filenameRegex.exec(disposition);
                    if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
                    var a = document.createElement('a');
                    var url = window.URL.createObjectURL(data);
                    a.href = url;
                    a.download = filename;
                    document.body.append(a);
                    a.click();
                    a.remove();
                    window.URL.revokeObjectURL(url);
                }
                else {
                    getErrorToastMessage("Production file for order line " + list[i].orderLineId + " does not exist");
                }
                i = i + 1;
                if (i < max) {
                    DownloadFile(list);
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {

            },
            complete: function () {
                if(i===max)
                hidePreloader();
            }
        });
    }

C # MVC:

 [HttpPost]
 [ValidateAntiForgeryToken]
public IActionResult OpenFile(OrderLineSimpleModel model)
        {
            byte[] file = null;

            try
            {
                if (model != null)
                {
                    //code for getting file from api - part is missing here as not important for this example
                    file = apiHandler.Get<byte[]>(downloadApiUrl, token);

                    var contentDispositionHeader = new System.Net.Mime.ContentDisposition
                    {
                        Inline = true,
                        FileName = fileName
                    };
                    //    Response.Headers.Add("Content-Disposition", contentDispositionHeader.ToString() + "; attachment");
                    Response.Headers.Add("Content-Type", "application/pdf");
                    Response.Headers.Add("Content-Disposition", "attachment; filename=" + fileName);
                    Response.Headers.Add("Content-Transfer-Encoding", "binary");
                    Response.Headers.Add("Content-Length", file.Length.ToString());

                }
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "Error getting pdf", null);
                return Ok();
            }

            return File(file, System.Net.Mime.MediaTypeNames.Application.Pdf);
        }

Пока вы возвращаете ответ 200, успех в Ajax может работать с ним, вы можете проверить, существует ли файл на самом деле или нет, так как строка в этом случае будет ложной, и вы можете сообщить пользователю об этом:

 if (disposition && disposition.indexOf('attachment') !== -1) {

0

Мне нужно было решение, подобное @ alain-cruz, но в nuxt / vue с несколькими загрузками. Я знаю, что браузеры блокируют загрузку нескольких файлов, и у меня также есть API, который возвращает набор данных в формате CSV. Сначала я собирался использовать JSZip, но мне нужна была поддержка IE, так что вот мое решение. Если кто-то может помочь мне улучшить это, это было бы здорово, но пока это работает для меня.

API возвращает:

data : {
  body: {
    fileOne: ""col1", "col2", "datarow1.1", "datarow1.2"...so on",
    fileTwo: ""col1", "col2"..."
  }
}

page.vue:

<template>
  <b-link @click.prevent="handleFileExport">Export<b-link>
</template>

export default = {
   data() {
     return {
       fileNames: ['fileOne', 'fileTwo'],
     }
   },
  computed: {
    ...mapState({
       fileOne: (state) => state.exportFile.fileOne,
       fileTwo: (state) => state.exportFile.fileTwo,
    }),
  },
  method: {
    handleExport() {
      //exportFileAction in store/exportFile needs to return promise
      this.$store.dispatch('exportFile/exportFileAction', paramsToSend)
        .then(async (response) => {
           const downloadPrep = this.fileNames.map(async (fileName) => {
           // using lodash to get computed data by the file name
           const currentData = await _.get(this, `${fileName}`);
           const currentFileName = fileName;
           return { currentData, currentFileName };
         });
         const response = await Promise.all(downloadPrep);
         return response;
       })
       .then(async (data) => {
         data.forEach(({ currentData, currentFileName }) => {
           this.forceFileDownload(currentData, currentFileName);
         });
       })
       .catch(console.error);
    },
    forceFileDownload(data, fileName) {
     const url = window.URL
         .createObjectURL(new Blob([data], { type: 'text/csv;charset=utf-8;' }));
     const link = document.createElement('a');
     link.href = url;
     link.setAttribute('download', `${fileName}.csv`);
     document.body.appendChild(link);
     link.click();
   },
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.