Ajax를 사용하여 PDF 파일 다운로드 및 열기


98

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

위의 오류는 다음과 같습니다.

브라우저에서이 서버가 이해할 수없는 요청을 보냈습니다.

답변:


37

이를 위해 반드시 Ajax가 필요하지는 않습니다. 그냥 <a>당신이 설정 한 경우 링크는 충분하다 content-dispositionattachment서버 측 코드에서. 이렇게하면 부모 페이지가 열려있는 상태로 유지됩니다 (그게 주요 관심사 인 경우에는 왜 필요하지 않게 Ajax를 선택했을까요?). 게다가, 이것을 무심코 잘 처리 할 방법이 없습니다. PDF는 문자 데이터가 아닙니다. 바이너리 데이터입니다. 당신은 같은 일을 할 수 없습니다 $(element).load(). 완전히 새로운 요청 을 사용하고 싶습니다 . 그것은 <a href="pdfservlet/filename.pdf">pdf</a>완벽하게 적합합니다.

서버 측 코드에 대해 더 많은 도움을 받으려면 사용 된 언어에 대해 자세히 설명하고 코드 시도의 일부를 게시해야합니다.


7
다시 한 번 : 이를 위해 Ajax가 필요 하지 않습니다 . 문제를 요구할뿐입니다. PDF는 HTML이나 JSON과 같은 문자 데이터가 아니라 바이너리 데이터입니다.
BalusC

3
var url = contextPath + "/xyz/blahBlah.action"; URL + = URL + "?" + 매개 변수; 시도 {var child = window.open (url); child.focus (); } catch (e) {}
Nayn 2010-01-04

5
일부 브라우저에서는 window.open이 열려 있고 비어있어 최종 사용자에게 불편할 수 있습니다. 따라서 window.open을 사용하지 마십시오. 이 경우 content-disposition에 설정되어 attachment, 당신은 단지 얻을 것이다 Save as대화를. 상위 페이지는 변경되지 않습니다. 그냥 <a href="pdfservlet/filename.pdf">pdf</a>또는 a <form action="pdfservlet/filename.pdf"><input type="submit"></form>로 충분합니다.
BalusC 2010 년

5
제한된 URL 길이가 있습니다. 그리고 저자는 POST에 대해 묻습니다.
Edward Olamisan

3
@EdwardOlamisan과 동의하십시오. 저자가 POST데이터를 시도했기 때문에 이것은 정답이 아닙니다 .
adamj 2016 년

122

이 작업을 수행하는 방법은 다음과 같습니다.

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

download.js를 사용하여 답변 업데이트

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


29
크롬에서 작동합니까? 빈 pdf 만 볼 수 있습니다.
Tarun Gupta

1
예, 모든 최신 브라우저에서 작동합니다. 빈 PDF가 보이면 새 탭에서 ajax URL을 실행 해보십시오. 빈 화면이 표시되면 pdf 자체에 문제가있을 수 있습니다. 다운로드 한 파일이 아닌 pdf 파일이있는 경우 이메일로 알려주십시오. :)
Mayur Padshala 2015

5
이 (앵커 요소)는 실제로 IE 11, Edge 및 Firefox에서 작동하지 않았습니다. "window.open (URL.createObjectURL (blob))"을 사용하여 성공을 변경하면 작동했습니다.
JimiSweden

3
pdf 파일이 다운로드되었지만 콘텐츠를 사용할 수 없습니다. 나는 서버 측에 byte []를 저장하고 pdf 내용을 사용할 수 있습니다. plz 제안합니다.
Awanish Kumar

4
빈 pdf 파일이 다운로드됩니다.
Farukh

31

나는 과거의 답변 중 어느 것도 원래 포스터의 문제를 발견했다고 생각하지 않습니다. 그들은 모두 포스터가 데이터를 POST하고 응답으로 다운로드를 시도하는 동안 GET 요청을 가정합니다.

더 나은 답을 찾는 과정에서 우리는 Ajax와 유사한 파일 다운로드 요청을위한jQuery 플러그인을 발견했습니다 .

"하트"에서 주어진 데이터를 입력 필드로 포함하는 "임시"HTML 양식을 생성합니다. 이 양식은 문서에 추가되고 원하는 URL에 게시됩니다. 그 직후 양식이 다시 제거됩니다.

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

Mayur의 답변 업데이트 는 내가 언급 한 jQuery 플러그인과 비교할 때 매우 유망하고 매우 간단합니다.


9

이것이 내가이 문제를 해결하는 방법입니다. 이 게시물
에 대한 Jonathan Amend의 답변은 저에게 많은 도움 이되었습니다 .
아래 예는 단순화되었습니다.

자세한 내용은 위의 소스 코드 에서 JQuery Ajax 요청 (GET, POST, PUT 등)을 사용하여 파일다운로드 할 수 있습니다. 또한 매개 변수를 JSON 으로 업로드 하고 콘텐츠 유형을 application / 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>  

두 개의 입력 텍스트, 하나의 선택 및 단추 요소가있는 간단한 양식.

자바 스크립트 페이지 소스 :

<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 클래스 소스 :

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 라이브러리에 추가했습니다. 재사용이 가능합니다. 도움이 되었기를 바랍니다.


2
Blob개체는 IE10 +에서 지원됩니다.
crush

나는 responseTypexhr 을 설정 arraybuffer하거나 blob이것이 작동 하도록 설정 해야했습니다. (그렇지 않으면 잘 작동합니다.)
tjklemz 2015 년

나는 똑같은 질문을했습니다. "그냥 링크 만들기"라고 대답하는 모든 사람들은 OP에 도움이되지 않습니다. 콘텐츠가 동적이고 링크가 동적이면 모든 것을 jquery해야합니다 ... 제 대답은 모든 숨겨진 입력 (사용자에게 표시되지 않음)이있는 페이지에 양식을 넣는 것이 었습니다. 그런 다음 작성하고 jquery로 제출하십시오. 잘 작동합니다.
Scott

이것은 훌륭한 대답이지만 어떤 이유로 빈 PDF가 계속 깨집니다. 알아낼 수 없습니다. API를 통해 동일한 바이트 셋을 반환하면 괜찮습니다. 따라서 MVC 응답과 관련이 있습니다. FileResult 응답 유형을 사용합니다. File (bytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
Jurijs Kastanovs 2019

설명 : 주소 표시 줄을 통해 URL을 열면 파일이 올바르게 열립니다. AJAX + blob을 사용하여 파일을 가져 오면 파일 형식이 잘못되었습니다.
Jurijs Kastanovs

7

나를 위해 일한 것은 서버 기능이 검색하기 때문에 다음 코드입니다. 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();
                }
});

이이 댓글의 시간에 완벽하게 나를 위해 작동하고 최신 크롬
Loredra L

6

이 플러그인을 사용하여 양식을 작성하고 제출 한 다음 페이지에서 제거 할 수 있습니다.

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

이것은 나를 위해 일했습니다. 여기 에서이 플러그인을 찾았 습니다.


이 플러그인은 양식을 생성하고 제출 한 다음 페이지에서 제거합니다. (누군가 궁금하다면)
crush

4

다음 코드가 저에게 효과적이었습니다.

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

4

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

3

Mayur Padshala가 제공 한 답변과 관련하여 이것은 ajax를 통해 pdf 파일을 다운로드하는 올바른 논리이지만 다른 사람들이 의견을보고함에 따라이 솔루션은 실제로 빈 pdf를 다운로드합니다.

이에 대한 이유는이 질문에 대한 답변에 설명되어 있습니다 . jQuery는 아직 일부 HTML5 XHR v2 기능을 구현하지 않았기 때문에 AJAX 요청을 사용하여 바이너리 데이터를로드하는 데 몇 가지 문제가 있습니다.이 개선 요청 및이 토론을 참조하십시오. .

따라서 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();
}

2

숨겨진 iframe을 만든 다음 위의 ajax 코드에서 :

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

그리고 제거 window.open(response);


이 솔루션은 매력처럼 작동했습니다. curl을 통해 파일을 가져 오는 서비스에 대한 curl 호출을 만드는 서버 측 스크립트를 호출하고 있습니다. 이것은 로딩 gif를 삭제하고 요청 링크를 비활성화 할 수 있기 때문에 훌륭하게 작동합니다.
eggmatters

1
이 솔루션은 원래 게시물에서와 같이 POST 요청이 아닌 GET 요청에 대해 작동합니다.
chiccodoro

2

이 스 니펫은 동일한 문제에 직면하게 될 각도 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 () {

});

답변에 대한 설명을 적어주세요.
Gufran Hasan 2018

1

Ajax로해야합니까? iframe에로드 할 수 없을까요?


1
나는 이것이 Ajax로 할 수 있는지 확인하고 있습니다. 기술적으로 불가능하거나 열등한 접근 방식이라면 다른 접근 방식으로 전환 할 것입니다.
Nayn 2010 년

1

이것이 몇 시간을 절약하고 두통에서 벗어날 수 있기를 바랍니다. 이것을 알아내는 데 시간이 좀 걸렸지 만 정기적으로 $ .ajax () 요청을하면 내 PDF 파일이 망가졌고 주소 표시 줄을 통해 요청하면 완벽하게 작동했습니다. 해결책은 다음과 같습니다.

download.js 포함 : http://danml.com/download.html

그런 다음 $ .ajax () 요청 대신 XMLHttpRequest를 사용하십시오.

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

0

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


0

우리와 같이 파일 스트림 (물리적으로 저장된 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>

자바 스크립트

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

로 인해 타겟 = "PDF 다운로드 출력" 응답은 iframe이 기입되고, 그러므로 어떤 페이지 새로 고침이 실행되지 않지만 PDF 응답 스트림이 다운로드 등 브라우저에서 출력된다.


죄송합니다. transferData 값을 어떻게 얻습니까?
Kate
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.