[javascript] jQuery.Ajax로 파일 다운로드

파일 다운로드를 위해 서버 측에 Struts2 조치가 있습니다.

<action name="download" class="com.xxx.DownAction">
    <result name="success" type="stream">
        <param name="contentType">text/plain</param>
        <param name="inputName">imageStream</param>
        <param name="contentDisposition">attachment;filename={fileName}</param>
        <param name="bufferSize">1024</param>
    </result>
</action>

그러나 jQuery를 사용하여 액션을 호출하면 :

$.post(
  "/download.action",{
    para1:value1,
    para2:value2
    ....
  },function(data){
      console.info(data);
   }
);

Firebug에서 바이너리 스트림으로 데이터가 검색되는 것을 볼 수 있습니다. 사용자가 파일을 로컬로 저장할 수 있는 파일 다운로드 창 을 여는 방법이 궁금합니다 .



답변

2019 최신 브라우저 업데이트

이것은 몇 가지주의 사항으로 권장되는 접근법입니다.

  • 비교적 현대적인 브라우저가 필요합니다
  • 파일이 매우 클 것으로 예상되는 경우 아래 작업 중 일부는 다운로드중인 파일 및 / 또는 기타 흥미로운 CPU만큼 큰 시스템 메모리를 소비 할 수 있으므로 원래 접근 방식 (iframe 및 쿠키)과 유사한 작업을 수행해야합니다. 부작용.
fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(resp => resp.blob())
  .then(blob => {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    // the filename you want
    a.download = 'todo-1.json';
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);
    alert('your file has downloaded!'); // or you know, something with better UX...
  })
  .catch(() => alert('oh no!'));

2012 독자적인 jQuery / iframe / 쿠키 기반 접근

Bluish 는 이것에 대해 완전히 맞습니다. JavaScript는 파일을 사용자의 컴퓨터에 직접 저장할 수 없기 때문에 Ajax를 통해 할 수 없습니다 (보안 문제가 없음). 불행히도 파일 다운로드시 기본 창의 URL을 가리키면 파일 다운로드 발생시 사용자 경험을 거의 제어 할 수 없습니다.

더 나은 사용자 경험을 제공하기 위해 OnSuccess 및 OnFailure 콜백으로 완료된 파일 다운로드로 “Ajax like”환경을 허용하는 jQuery File Download 를 작성했습니다 . 플러그인이 해결하는 일반적인 문제와 플러그인을 사용하는 몇 가지 방법 및 jQuery 파일 다운로드 데모 데모 에 대한 내 블로그 게시물 을 살펴보십시오 . 여기는 소스가 있습니다

다음은 약속과 함께 플러그인 소스 를 사용하는 간단한 유스 케이스 데모 입니다. 데모 페이지 뿐만 아니라 많은 다른, ‘더 나은 UX’의 예를 포함한다.

$.fileDownload('some/file.pdf')
    .done(function () { alert('File download a success!'); })
    .fail(function () { alert('File download failed!'); });

지원해야하는 브라우저에 따라 https://github.com/eligrey/FileSaver.js/ 를 사용하여 IFRAME 메소드 jQuery File Download가 사용하는 것보다 더 명시 적으로 제어 할 수 있습니다.


답변

Noone @ Pekka ‘s solution 님을 포스팅했습니다. 게시하지 않았으므로 게시하겠습니다. 누군가를 도울 수 있습니다.

Ajax를 통해이 작업을 수행 할 필요는 없습니다. 그냥 사용

window.location="download.action?para1=value1...."


답변

HTML5로 할 수 있습니다

NB : 반환 된 파일 데이터는 JSON 인코딩 이진 데이터를 사용할 수 없으므로 base64로 인코딩해야합니다.

AJAX대답에는 다음과 같은 데이터 구조가 있습니다.

{
    result: 'OK',
    download: {
        mimetype: string(mimetype in the form 'major/minor'),
        filename: string(the name of the file to download),
        data: base64(the binary data as base64 to download)
    }
}

즉, AJAX를 통해 파일을 저장하기 위해 다음을 수행 할 수 있습니다.

var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
    // Do it the HTML5 compliant way
    var blob = base64ToBlob(result.download.data, result.download.mimetype);
    var url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = result.download.filename;
    a.click();
    window.URL.revokeObjectURL(url);
}

base64ToBlob 함수는 여기 에서 가져 왔으며이 함수 를 준수하여 사용해야합니다.

function base64ToBlob(base64, mimetype, slicesize) {
    if (!window.atob || !window.Uint8Array) {
        // The current browser doesn't have the atob function. Cannot continue
        return null;
    }
    mimetype = mimetype || '';
    slicesize = slicesize || 512;
    var bytechars = atob(base64);
    var bytearrays = [];
    for (var offset = 0; offset < bytechars.length; offset += slicesize) {
        var slice = bytechars.slice(offset, offset + slicesize);
        var bytenums = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            bytenums[i] = slice.charCodeAt(i);
        }
        var bytearray = new Uint8Array(bytenums);
        bytearrays[bytearrays.length] = bytearray;
    }
    return new Blob(bytearrays, {type: mimetype});
};

서버가 저장할 파일 데이터를 덤프하는 경우에 좋습니다. 그러나 HTML4 대체 방법을 구현하는 방법을 잘 찾지 못했습니다.


답변

1. 프레임 워크에 무관 : 서블릿 다운로드 파일을 첨부 파일로

<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>

2. Struts2 프레임 워크 : 첨부 파일로 활동 다운로드 파일

<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>

<s:a>태그로 작성된 URLOGNL 로 가리키는 태그 를 사용하는 것이 좋습니다 .<s:url>

<!-- without JS, with Struts tags: THE RIGHT WAY -->
<s:url action="downloadAction.action" var="url">
    <s:param name="param1">value1</s:param>
</s:ulr>
<s:a href="%{url}" >download</s:a>

위의 경우에, 당신이 필요 작성하는 내용 – 처리 받는 헤더를 응답 파일에 필요한 다운로드 (할 것을 지정 attachment) 및 브라우저가 열리지 않습니다 ( inline). 당신이 필요로 지정하기 위해 컨텐츠 유형을 너무, 당신은 (도움말을 실제 진행 막대 그리기 브라우저를) 파일 이름과 길이를 추가 할 수 있습니다.

예를 들어 ZIP을 다운로드 할 때 :

response.setContentType("application/zip");
response.addHeader("Content-Disposition",
                   "attachment; filename=\"name of my file.zip\"");
response.setHeader("Content-Length", myFile.length()); // or myByte[].length...

Struts2를 사용하면 ( 예를 들어 Action을 서블릿으로 사용 하지 않고 직접 스트리밍 을위한 핵 을 사용 하지 않는 한) 응답에 직접 아무것도 쓸 필요가 없습니다. 단순히 사용하여 스트림 결과 유형을 작동하고 struts.xml에서 그것을 구성 : 예를

<result name="success" type="stream">
   <param name="contentType">application/zip</param>
   <param name="contentDisposition">attachment;filename="${fileName}"</param>
   <param name="contentLength">${fileLength}</param>
</result>

3. 프레임 워크에 구애받지 않음 (/ Struts2 프레임 워크) : 브라우저 내에서 파일을 여는 서블릿 (/ Action)

파일을 다운로드하는 대신 브라우저 내에서 파일을 열려면 Content-dispositioninline 으로 설정해야 하지만 대상은 현재 창 위치가 될 수 없습니다. 자바 스크립트로 작성된 새 창 <iframe>, 페이지에서 또는 “토론 된”target = “_ blank”를 사용하여 즉석에서 만든 새 창을 타겟팅해야합니다 .

<!-- From a parent page into an IFrame without javascript -->
<a href="downloadServlet?param1=value1" target="iFrameName">
    download
</a>

<!-- In a new window without javascript -->
<a href="downloadServlet?param1=value1" target="_blank">
    download
</a>

<!-- In a new window with javascript -->
<a href="javascript:window.open('downloadServlet?param1=value1');" >
    download
</a>


답변

브라우저에서 파일을 다운로드하는 간단한 방법은 다음과 같이 요청하는 것입니다.

 function downloadFile(urlToSend) {
     var req = new XMLHttpRequest();
     req.open("GET", urlToSend, true);
     req.responseType = "blob";
     req.onload = function (event) {
         var blob = req.response;
         var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
         var link=document.createElement('a');
         link.href=window.URL.createObjectURL(blob);
         link.download=fileName;
         link.click();
     };

     req.send();
 }

브라우저 다운로드 팝업이 열립니다.


답변

해결 방법 솔루션으로 거의 기능을 만들지 않았습니다 (@JohnCulviner 플러그인에서 영감을 얻음).

// creates iframe and form in it with hidden field,
// then submit form with provided data
// url - form url
// data - data to form field
// input_name - form hidden input name

function ajax_download(url, data, input_name) {
    var $iframe,
        iframe_doc,
        iframe_html;

    if (($iframe = $('#download_iframe')).length === 0) {
        $iframe = $("<iframe id='download_iframe'" +
                    " style='display: none' src='about:blank'></iframe>"
                   ).appendTo("body");
    }

    iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
    if (iframe_doc.document) {
        iframe_doc = iframe_doc.document;
    }

    iframe_html = "<html><head></head><body><form method='POST' action='" +
                  url +"'>" +
                  "<input type=hidden name='" + input_name + "' value='" +
                  JSON.stringify(data) +"'/></form>" +
                  "</body></html>";

    iframe_doc.open();
    iframe_doc.write(iframe_html);
    $(iframe_doc).find('form').submit();
}

클릭 이벤트가있는 데모 :

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2}, 'dataname');
});


답변

나는 같은 문제에 직면하여 성공적으로 해결했습니다. 내 유스 케이스는 이것입니다.

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

위의 스 니펫은 다음과 같습니다.

  • XMLHttpRequest를 사용하여 서버에 배열을 JSON으로 게시
  • blob (이진)으로 콘텐츠를 가져온 후 다운로드 가능한 URL을 만들어 보이지 않는 “a”링크에 첨부 한 다음 클릭합니다. 나는 여기에 POST 요청을했다. 대신 간단한 GET도 가능합니다. Ajax를 통해 파일을 다운로드 할 수 없으며 XMLHttpRequest를 사용해야합니다.

여기서는 서버 측에서 몇 가지 사항을 신중하게 설정해야합니다. Python Django HttpResponse에서 헤더를 거의 설정하지 않았습니다. 다른 프로그래밍 언어를 사용하는 경우 적절하게 설정해야합니다.

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

여기서 xls (excel)를 다운로드 했으므로 contentType을 1 이상으로 조정했습니다. 파일 형식에 따라 설정해야합니다. 이 기술을 사용하여 모든 종류의 파일을 다운로드 할 수 있습니다.