Загрузите и откройте PDF файл, используя Ajax

У меня есть класс действий, который генерирует PDF. contentType установлен соответствующим образом.

public class MyAction extends ActionSupport 
{
   public String execute() {
    ...
    ...
    File report = signedPdfExporter.generateReport(xyzData, props);

    inputStream = new FileInputStream(report);
    contentDisposition = "attachment=\"" + report.getName() + "\"";
    contentType = "application/pdf";
    return SUCCESS;
   }
}

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

$.ajax({
    type: "POST",
    url: url,
    data: wireIdList,
    cache: false,
    success: function(response)
    {
        alert('got response');
        window.open(response);
    },
    error: function (XMLHttpRequest, textStatus, errorThrown) 
    {
        alert('Error occurred while opening fax template' 
              + getAjaxErrorString(textStatus, errorThrown));
    }
});

Вышесказанное дает ошибку:

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

Ответ 1

Для этого вам не обязательно Ajax. Достаточно ссылки <a> достаточно, если вы установите content-disposition в attachment в код на стороне сервера. Таким образом, родительская страница останется открытой, если это была ваша главная проблема (почему бы вам без необходимости выбрать Ajax для этого в противном случае?). Кроме того, нет никакого способа справиться с этим асинхронно. PDF не является символьным. Это двоичные данные. Вы не можете делать такие вещи, как $(element).load(). Вы хотите использовать полностью новый запрос для этого. Для этого <a href="pdfservlet/filename.pdf">pdf</a> отлично подходит.

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

Ответ 2

Вот как я получил это работает

$.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();
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Ответ 3

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

В ходе поиска лучшего ответа мы обнаружили этот jQuery плагин для запроса Ajax файлов загрузки файлов.

В своем "сердце" он создает "временную" форму HTML, содержащую данные в качестве полей ввода. Эта форма добавляется к документу и отправляется на нужный URL. Сразу после этого форма снова удаляется:

jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
    .appendTo('body').submit().remove()

Обновление Майур ответ выглядит довольно многообещающим и очень простым по сравнению с подключаемым модулем jQuery, о котором я говорил.

Ответ 4

Вот как я решаю эту проблему.
Ответ Джонатана Аменда на этот пост мне очень помог.
Пример ниже упрощен.

Для получения более подробной информации приведенный выше исходный код способен загружать файл с помощью запроса JQuery Ajax (GET, POST, PUT и т.д.). Это также помогает загружать параметры как JSON и изменять тип контента для приложения /json (по умолчанию).

Источник html:

<form method="POST">
    <input type="text" name="startDate"/>
    <input type="text" name="endDate"/>
    <input type="text" name="startDate"/>
    <select name="reportTimeDetail">
        <option value="1">1</option>
    </select>
    <button type="submit"> Submit</button>
</form>  

Простая форма с двумя входными текстами, одним элементом выбора и элементом кнопки.

Источник javascript:

<script type="text/javascript" src="JQuery 1.11.0 link"></script>
<script type="text/javascript">
    // File Download on form submition.
    $(document).on("ready", function(){
        $("form button").on("click", function (event) {
            event.stopPropagation(); // Do not propagate the event.

            // Create an object that will manage to download the file.
            new AjaxDownloadFile({
                url: "url that returns a file",
                data: JSON.stringify($("form").serializeObject())
            });

            return false; // Do not submit the form.
        });
    });
</script>  

Простое событие при нажатии кнопки. Он создает объект AjaxDownloadFile. Ниже приведен источник AjaxDownloadFile.

Источник AjaxDownloadFile class:

var AjaxDownloadFile = function (configurationSettings) {
    // Standard settings.
    this.settings = {
        // JQuery AJAX default attributes.
        url: "",
        type: "POST",
        headers: {
            "Content-Type": "application/json; charset=UTF-8"
        },
        data: {},
        // Custom events.
        onSuccessStart: function (response, status, xhr, self) {
        },
        onSuccessFinish: function (response, status, xhr, self, filename) {
        },
        onErrorOccured: function (response, status, xhr, self) {
        }
    };
    this.download = function () {
        var self = this;
        $.ajax({
            type: this.settings.type,
            url: this.settings.url,
            headers: this.settings.headers,
            data: this.settings.data,
            success: function (response, status, xhr) {
                // Start custom event.
                self.settings.onSuccessStart(response, status, xhr, self);

                // Check if a filename is existing on the response headers.
                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
                }

                // Final custom event.
                self.settings.onSuccessFinish(response, status, xhr, self, filename);
            },
            error: function (response, status, xhr) {
                // Custom event to handle the error.
                self.settings.onErrorOccured(response, status, xhr, self);
            }
        });
    };
    // Constructor.
    {
        // Merge settings.
        $.extend(this.settings, configurationSettings);
        // Make the request.
        this.download();
    }
};

Я создал этот класс для добавления в свою библиотеку JS. Он многоразовый. Надеюсь, что это поможет.

Ответ 5

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

jQuery.download = function(url, data, method) {
    //url and data options required
    if (url && data) {
        //data can be string of parameters or array/object
        data = typeof data == 'string' ? data : jQuery.param(data);
        //split params into form inputs
        var inputs = '';
        jQuery.each(data.split('&'), function() {
            var pair = this.split('=');
            inputs += '<input type="hidden" name="' + pair[0] +
                '" value="' + pair[1] + '" />';
        });
        //send request
        jQuery('<form action="' + url +
                '" method="' + (method || 'post') + '">' + inputs + '</form>')
            .appendTo('body').submit().remove();
    };
};


$.download(
    '/export.php',
    'filename=mySpreadsheet&format=xls&content=' + spreadsheetData
);

Это сработало для меня. Нашел этот плагин здесь

Ответ 6

Что для меня работало, это следующий код, так как серверная функция извлекает File(memoryStream.GetBuffer(), "application/pdf", "fileName.pdf");:

$http.get( fullUrl, { responseType: 'arraybuffer' })
            .success(function (response) {
                var blob = new Blob([response], { type: 'application/pdf' });

                if (window.navigator && window.navigator.msSaveOrOpenBlob) {
                    window.navigator.msSaveOrOpenBlob(blob); // for IE
                }
                else {
                    var fileURL = URL.createObjectURL(blob);
                    var newWin = window.open(fileURL);
                    newWin.focus();
                    newWin.reload();
                }
});

Ответ 7

создайте скрытый iframe, затем в вашем ajax-коде выше:

url: document.getElementById('myiframeid').src = your_server_side_url,

и удалите window.open(response);

Ответ 8

Вам нужно сделать это с помощью Ajax? Невозможно ли загрузить его в iframe?

Ответ 9

Этот фрагмент предназначен для пользователей с угловыми js, которые будут сталкиваться с одной и той же проблемой. Обратите внимание, что файл ответов загружается с помощью запрограммированного события кликов. В этом случае заголовки были отправлены сервером, содержащим имя файла и тип контента/типа.

$http({
    method: 'POST', 
    url: 'DownloadAttachment_URL',
    data: { 'fileRef': 'filename.pdf' }, //I'm sending filename as a param
    headers: { 'Authorization': $localStorage.jwt === undefined ? jwt : $localStorage.jwt },
    responseType: 'arraybuffer',
}).success(function (data, status, headers, config) {
    headers = headers();
    var filename = headers['x-filename'];
    var contentType = headers['content-type'];
    var linkElement = document.createElement('a');
    try {
        var blob = new Blob([data], { type: contentType });
        var url = window.URL.createObjectURL(blob);

        linkElement.setAttribute('href', url);
        linkElement.setAttribute("download", filename);

        var clickEvent = new MouseEvent("click", {
            "view": window,
            "bubbles": true,
            "cancelable": false
        });
        linkElement.dispatchEvent(clickEvent);
    } catch (ex) {
        console.log(ex);
    }
}).error(function (data, status, headers, config) {
}).finally(function () {

});

Ответ 10

Что касается ответа Майра Падшалы, это правильная логика для загрузки файла PDF через ajax, но, как сообщают другие пользователи в комментариях, это решение действительно загружает пустой pdf файл.

Причина этого объясняется в принятом ответе на этот вопрос: jQuery имеет некоторые проблемы, загружающие двоичные данные с использованием запросов AJAX, поскольку он еще не реализует некоторые возможности HTML5 XHR v2, см. Этот запрос расширения и это обсуждение.

Поэтому, используя HTMLHTTPRequest код должен выглядеть так:

var req = new XMLHttpRequest();
req.open("POST", "URL", true);
req.responseType = "blob";
req.onload = function (event) {
    var blob = req.response;
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="name_for_the_file_to_save_with_extention";
    link.click();
}

Ответ 11

Следующий код работал для меня

//Parameter to be passed
var data = 'reportid=R3823&isSQL=1&filter=[]';
var xhr = new XMLHttpRequest();
xhr.open("POST", "Reporting.jsp"); //url.It can pdf file path
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = "blob";
xhr.onload = function () {
    if (this.status === 200) {
        var blob = new Blob([xhr.response]);
        const url = window.URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.href = url;
        a.download = 'myFile.pdf';
        a.click();
        setTimeout(function () {
            // For Firefox it is necessary to delay revoking the ObjectURL
            window.URL.revokeObjectURL(data)
                , 100
        })
    }
};
xhr.send(data);

Ответ 12

Чтобы устранить пустую проблему PDF в пост-запросе для получения потоковых данных, таких как PDF, нам нужно добавить тип ответа как "arraybuffer" или "blob" в запросе

$.ajax({
  url: '<URL>',
  type: "POST",
  dataType: 'arraybuffer',
  success: function(data) {
    let blob = new Blob([data], {type: 'arraybuffer'});
    let link = document.createElement('a');
    let objectURL = window.URL.createObjectURL(blob);
    link.href = objectURL;
    link.target = '_self';
    link.download = "fileName.pdf";
    (document.body || document.documentElement).appendChild(link);
    link.click();
    setTimeout(()=>{
        window.URL.revokeObjectURL(objectURL);
        link.remove();
    }, 100);
  }
});

Ответ 13

var xhr;
var beforeSend = function(){
    $('#pleasewaitDL').modal('show');
}
$(function () {
    $('#print_brochure_link').click(function(){
        beforeSend();
        xhr = new XMLHttpRequest();
        xhr.open("GET",$('#preparedPrintModalForm').attr('action'), true); 
        xhr.responseType = "blob";
        xhr.onload = function (e) {
            if (this.status === 200) {
                var file = window.URL.createObjectURL(this.response);
                var a = document.createElement("a");
                a.href = file;
                a.download = this.response.name || "Property Brochure";
                console.log(file);
                document.body.appendChild(a);
                a.click();
                
                window.onfocus = function () {                     
                  document.body.removeChild(a)
                }
                $('#pleasewaitDL').modal('hide');
            };
        };
        xhr.send($('#preparedPrintModalForm').serialize());
    });
    $('#pleasewaitDLCancel').click(function() {
        xhr.abort();
    });
});

Ответ 14

Если вам нужно работать с файловым потоком (так нет физически сохраненного PDF), как мы делаем, и вы хотите загрузить PDF без перезагрузки страницы, для нас работает следующая функция:

HTML

<div id="download-helper-hidden-container" style="display:none">
     <form id="download-helper-form" target="pdf-download-output" method="post">
            <input type="hidden" name="downloadHelperTransferData" id="downloadHelperTransferData" />
     </form>
     <iframe id="pdf-helper-output" name="pdf-download-output"></iframe>
</div>

Javascript

var form = document.getElementById('download-helper-form');
$("#downloadHelperTransferData").val(transferData);
form.action = "ServerSideFunctionWhichWritesPdfBytesToResponse";
form.submit();

Из-за target = "pdf-download-output" ответ записывается в iframe, и поэтому перезагрузка страницы не выполняется, но поток pdf-response выводится в браузере как загрузка.

Ответ 15

Вот подробный ответ на ваш вопрос. позвольте мне начать с кода на стороне сервера:

Класс ниже используется для создания pdf с произвольным содержимым и возврата эквивалентного байтового массива outputtream.

public class pdfgen extends AbstractPdfView{

 private static ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

public ByteArrayOutputStream showHelp() throws Exception {
    Document document = new Document();
   // System.IO.MemoryStream ms = new System.IO.MemoryStream();
    PdfWriter.getInstance(document,byteArrayOutputStream);
    document.open();
    document.add(new Paragraph("table"));
    document.add(new Paragraph(new Date().toString()));
    PdfPTable table=new PdfPTable(2);

    PdfPCell cell = new PdfPCell (new Paragraph ("table"));

    cell.setColspan (2);
    cell.setHorizontalAlignment (Element.ALIGN_CENTER);
    cell.setPadding (10.0f);
    //cell.setBackgroundColor (new BaseColor (140, 221, 8));                                  

    table.addCell(cell);                                    
    ArrayList<String[]> row=new ArrayList<String[]>();
    String[] data=new String[2];
    data[0]="1";
    data[1]="2";
    String[] data1=new String[2];
    data1[0]="3";
    data1[1]="4";
    row.add(data);
    row.add(data1);

    for(int i=0;i<row.size();i++) {
      String[] cols=row.get(i);
      for(int j=0;j<cols.length;j++){
        table.addCell(cols[j]);
      }
    }

    document.add(table);
    document.close();

    return byteArrayOutputStream;   
}

}

Затем следует код контроллера: здесь bytearrayoutputstream преобразуется в bytearray и отправляется на клиентскую сторону с использованием объекта ответа с соответствующими заголовками.

@RequestMapping(path="/home")
public ResponseEntity<byte[]> render(HttpServletRequest request , HttpServletResponse response) throws IOException
{
  pdfgen pg=new pdfgen();
    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment:filename=report.pdf");
    try {
            OutputStream out = response.getOutputStream();
    }
  catch (IOException e){
        e.printStackTrace();
    }
    byte[] contents = null;
    try {
        contents = pg.showHelp().toByteArray();
    } 
  catch (Exception e) {
        e.printStackTrace();
    }
  //These 3 lines are used to write the byte array to pdf file
  /*FileOutputStream fos = new FileOutputStream("/Users/naveen-pt2724/desktop/nama.pdf");
  fos.write(contents);
  fos.close();*/
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.parseMediaType("application/pdf"));
//Here you have to set the actual filename of your pdf
    String filename = "output.pdf";
    headers.setContentDispositionFormData(filename, filename);
    headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
    ResponseEntity<byte[]> respons = new ResponseEntity<byte[]>(contents, headers, HttpStatus.OK);
    return respons;
}

Заголовок должен быть установлен на "application/pdf"

Затем идет код на стороне клиента: где вы можете сделать ajax-запрос к серверу, чтобы открыть файл PDF в новой вкладке браузера

 $.ajax({
            url:'/PDFgen/home',
            method:'POST',
            cache:false,
             xhrFields: {
                    responseType: 'blob'
                  },
              success: function(data) {
                  //alert(data);
                let blob = new Blob([data], {type: 'application/pdf'}); //mime type is important here
                let link = document.createElement('a'); //create hidden a tag element
                let objectURL = window.URL.createObjectURL(blob); //obtain the url for the pdf file
                link.href = objectURL; // setting the href property for a tag
                link.target = '_blank'; //opens the pdf file in  new tab
                link.download = "fileName.pdf"; //makes the pdf file download
                (document.body || document.documentElement).appendChild(link); //to work in firefox
                link.click(); //imitating the click event for opening in new tab
              },
            error:function(xhr,stats,error){
                 alert(error);
            }  
        }); 

Ответ 16

Надеюсь, это сэкономит вам несколько часов и избавит от головной боли. Мне потребовалось некоторое время, чтобы понять это, но выполнение регулярного запроса $.ajax() разрушило мой PDF файл, в то время как запрос через адресную строку работал отлично. Решение было таким:

Включите download.js: http://danml.com/download.html

Затем используйте XMLHttpRequest вместо запроса $.ajax().

    var ajax = new XMLHttpRequest(); 

    ajax.open("GET", '/Admin/GetPdf' + id, true); 
    ajax.onreadystatechange = function(data) { 
        if (this.readyState == 4)
        {
            if (this.status == 200)
            {
                download(this.response, "report.pdf", "application/pdf");

            }
            else if (this.responseText != "")
            {
                alert(this.responseText);
            }
        }
        else if (this.readyState == 2)
        {
            if (this.status == 200)
            {
                this.responseType = "blob";
            }
            else
            {
                this.responseType = "text";
            }
        }
    };

    ajax.send(null);