Загрузите pdf при использовании ajax после проверки [duplicate]

Развертывание в ответе @ goodside:

В некоторых случаях вам может понадобиться наложить строку с нулями (например, коды fips или другие числовые факторы). В OSX / Linux:

> sprintf("%05s", "104")
[1] "00104"

Но поскольку sprintf() вызывает команду C sprintf() ОС, обсуждаемую здесь здесь , в Windows 7 вы получаете другой результат:

> sprintf("%05s", "104")
[1] "  104"

Итак, на машинах Windows работа вокруг:

> sprintf("%05d", as.numeric("104"))
[1] "00104"

324
задан jwfearn 23 September 2013 в 21:38
поделиться

14 ответов

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

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

93
ответ дан user 19 August 2018 в 00:11
поделиться
  • 1
    Как указано в вопросе: «Использование простой HTML-формы также не является вариантом». – Pavle Predic 18 April 2013 в 16:20
  • 2
    Нет, потому что использование обычного POST будет перемещаться по обозревателю по URL-адресу POST. Я не хочу удаляться от страницы. Я хочу выполнить запрос в фоновом режиме, обработать ответ и представить его клиенту. – Pavle Predic 18 April 2013 в 16:30
  • 3
    Если сервер отправляет обратно заголовки, как и другой ответ, он открывается в новом окне - я сделал это раньше. Он будет перемещаться только в том случае, если ваш серверный скрипт вернул HTML-код – user 18 April 2013 в 16:34
  • 4
    @PavlePredic вы в конечном итоге выяснили, как управлять обоими сценариями ответа, т. Е. Текстовым ответом JSON или ответом файла загрузки? – Web User 19 February 2015 в 22:15
  • 5
    Ответ непонятен, и предлагаемое решение не работает. – stack247 8 September 2016 в 21:11

Я использовал этот файл 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
ответ дан Community 19 August 2018 в 00:11
поделиться

Чтобы получить Jonathan Amends ответ для работы в Edge I сделал следующие изменения:

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 });

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

1
ответ дан fstrandner 19 August 2018 в 00:11
поделиться

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

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

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

2
ответ дан Jairo Miranda 19 August 2018 в 00:11
поделиться
  • 1
    Это не пост ajax, поскольку вопрос использует ajax – Nidhin David 23 May 2016 в 17:29

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

Редактировать 2017-09-28: Обновлено, чтобы использовать конструктор файлов, когда он доступен, поэтому работает в Safari> = 10.1.

Редактировать 2015-10-16: jQuery ajax не способен корректно обрабатывать двоичные ответы (не может установить responseType), поэтому лучше использовать простой вызов XMLHttpRequest.

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 = typeof File === 'function'
            ? new File([this.response], filename, { type: type })
            : 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 = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location = 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 = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
});
438
ответ дан Jonathan Amend 19 August 2018 в 00:11
поделиться
  • 1
    +1 для потрясающего решения только для javascript. К сожалению, большинству людей по-прежнему необходимо поддерживать несовременные браузеры. – Robin van Baalen 22 May 2014 в 13:00
  • 2
    Я получаю эту ошибку в Safari. «Не удалось загрузить ресурс: прервана загрузка кадра». Есть ли что-то, что мне нужно сделать специально для Safari? – juminoz 25 September 2014 в 19:47
  • 3
    Что такое «Современный браузер»? имею в виду? Как еще в IE это поддерживается? – Sean Anderson 30 December 2014 в 23:53
  • 4
    Просто обратите внимание, что простой ajax не может извлекать данные как blob, см. Обсуждение здесь stackoverflow.com/questions/17657184/… и здесь stackoverflow.com/questions/28438800/… – Anatoly 10 February 2015 в 21:01
  • 5
    На самом деле, кто-нибудь использовал это решение и нашел работу для Safari и IE9? – user1447679 30 April 2015 в 01:55

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

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

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

Если вы работаете с небольшими размерами файлов & lt; 1MB, вы этого не заметите.

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

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

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

Месяцы прошли с тех пор, как я опубликовал это, наконец, я «Мы нашли лучший подход к ускорению работы при работе с большими строками base64. Теперь я храню строки base64 в базе данных (используя longtext или longblog fields), затем передаю свой идентификатор записи через файл загрузки jQuery, наконец, в файле сценария загрузки я запрашиваю базу данных с помощью этого идентификатора, чтобы вытащить строку 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», которые не дали мне ответа, который я искал, я надеюсь, что эта информация поможет кому-то найти что-то подобное.

11
ответ дан José SAYAGO 19 August 2018 в 00:11
поделиться
  • 1
    jQuery File Download перенаправляет меня только на URL. Я называю это так: jQuery.download("api/ide/download-this-file.php", {filePath: path2Down}, "POST");. – Kin 13 March 2017 в 21:00

Какой серверный язык вы используете? В моем приложении я могу легко загрузить файл из вызова 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 в своем комментарии, он не будет перемещаться от текущей страницы.

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

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

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

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

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

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

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

Примечание

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

26
ответ дан jwfearn 19 August 2018 в 00:11
поделиться
  • 1
    Можете ли вы предоставить код jquery, который выполняет запрос POST? Обычно вы предоставляете функцию обратного вызова, которая имеет дело с ответом сервера, а затем вы анализируете и обрабатываете этот ответ в соответствии с требованиями вашего приложения. Как именно вы заставите браузер показать диалог загрузки, как только он получит ответ, который вы предоставляете, используя вышеуказанный код на стороне сервера? – Pavle Predic 18 April 2013 в 16:17
  • 2
    @PavlePredic обновил мой ответ с помощью клиентской обработки – Robin van Baalen 18 April 2013 в 16:43
  • 3
    Спасибо, я понимаю, что ты имел в виду сейчас. Интересное решение с iframe, однако оно не будет работать в моем сценарии. Как я уже сказал в вопросе: «Ответ может быть строкой JSON или может быть файлом (как вложением)» Изменение того, что возвращает сервер, не является вариантом. Спасибо за ваши усилия. Это хорошее решение и может помочь кому-то с подобной проблемой. Я поднимусь. – Pavle Predic 18 April 2013 в 17:01
  • 4
    Жаль, что я не мог помочь вам с этим решением. Является ли изменение запроса единственным, что вы не можете сделать, или не можете вообще написать какой-либо серверный код в этом конкретном случае? Если вы сами можете написать код на стороне сервера, вы можете написать прокси-сервер, который запрашивает файл и вместо него возвращает URL-адрес. – Robin van Baalen 18 April 2013 в 18:18
  • 5
    @Robin van Baalen Это был отличный ответ для меня, спасибо. Я использую совершенно разные рамки, но принцип звучит. Upvoted. – JSager 16 October 2013 в 17:14

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

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

$.redirect(url, [values, [method, [target]]])
2
ответ дан KurtPreston 19 August 2018 в 00:11
поделиться

Вот мое решение, используя временную скрытую форму.

//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.

2
ответ дан Ludovic Martin 19 August 2018 в 00:11
поделиться

Вот как я получил эту работу 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>")
});

7
ответ дан Mayur Padshala 19 August 2018 в 00:11
поделиться

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

«Опубликовать данные 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.
  • После извлечения содержимого в виде blob (двоичного) мы создаем загружаемый URL-адрес и прикрепляем его к невидимой ссылке «a», а затем щелкаем по ней.

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

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

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

25
ответ дан Naren Yellavula 19 August 2018 в 00:11
поделиться

см.: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/ он вернет blob в качестве ответа, который затем может быть помещен в filesaver

1
ответ дан Samantha Adrichem 19 August 2018 в 00:11
поделиться

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

  1. Вы не можете установить заголовки в запросе. Если ваша схема аутентификации включает заголовки, Json-Web-Token прошел в заголовке Authorization, вам придется найти другой способ ее отправки, например, в качестве параметра запроса.
  2. Вы не можете реально сообщите, когда запрос завершен. Ну, вы можете использовать cookie, который устанавливается в ответ, как это сделано jquery.fileDownload , но это FAR от совершенства.
  3. Если сервер отвечает на ошибку, пользователь будет перенаправлен на страницу с ошибкой.
  4. Если сервер отвечает на ошибку, Вы можете использовать только типы контента, поддерживаемые формой . Это означает, что вы не можете использовать JSON.

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

5
ответ дан tepez 19 August 2018 в 00:11
поделиться
  • 1
    СПАСИБО, МУЖИК!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! – sergioBertolazzo 16 June 2017 в 14:20

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

$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();
});
17
ответ дан Tim Hettler 19 August 2018 в 00:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: