jQuery.Ajax로 파일 다운로드


420

파일 다운로드를 위해 서버 측에 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에서 바이너리 스트림으로 데이터가 검색되는 것을 볼 수 있습니다. 사용자가 파일을 로컬로 저장할 수 있는 파일 다운로드 창 을 여는 방법이 궁금합니다 .



1
솔루션이 동일하다는 것을 알 수있는 한 플랫폼 차이에도 불구하고 중복으로 표시했습니다 (Ajax를 통해이 작업을 수행 할 수 없으며 필요하지 않음).
Pekka

1
따라서 아약스없이 window.location = "download.action? para1 = value1 ...."?
hguser

답변:


676

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가 사용하는 것보다 더 명시 적으로 제어 할 수 있습니다.


69
나는 당신이 구축 한 것을 좋아하지만 더 많은 StackOverFlow 크레딧을 얻으려면 여기에 귀하의 답변에 좀 더 자세한 내용이 포함되어야한다고 생각합니다. 특히 문제를 해결 한 방법에 대해.
AnthonyVO

14
이 "플러그인"이 어떻게 한계를 극복하는지 정확하게 언급한다면 블로그 / 플러그인 소스로 가서 강제로 보지 않아도됩니다. 예를 들어, 대신 iframe에 게시하고 있습니까? 대신 원격 스크립트가 파일을 저장하고 URL을 반환해야합니까?
케빈 B

2
@asgerhallas 물론이지만 링크가 사라지면 완전히 쓸모가 없습니다.
Kevin B

26
플러그인 사용 방법 및 작동 방식에 대한 자세한 설명을 블로그에 게시하는 것이 훨씬 좋습니다. 하지만 최소한이 플러그인이 문제를 해결하는 방법에 대한 간단한 개요를 제공했을 수 있습니다. 예를 들어, 서버가 쿠키를 설정하고 쿠키가 존재할 때까지 자바 스크립트가 쿠키를 계속 찾도록하여 문제를 해결합니다. 일단 존재하면 다운로드가 완료된 것으로 가정 할 수 있습니다. 이러한 종류의 정보를 사용하면 자체 솔루션을 매우 빠르게 롤백 할 수 있으며 블로그 / 플러그인 / jquery에 더 이상 100 % 의존하지 않고 다른 라이브러리에 적용 할 수 있습니다.
Kevin B

1
Royi, 내가 아는 것처럼 AJAX는 파일 다운로드를 지원하여 디스크에 저장하는 파일 다운로드 팝업을 만들 수 없습니다 . 내가 모르는 방법을 찾았습니까?
John Culviner

227

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

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

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

4
좋은 점은 ... 다운로드 파일 프롬프트를 처리하고 jquery ajax를 사용하는 데 어려움을 겪고 있었기 때문에이 솔루션은 완벽하게 작동합니다 .. +1
swapnesh

45
이를 위해서는 서버가 Content-Disposition 헤더 값인 'attachment'를 설정해야합니다. 그렇지 않으면 브라우저가 응답 콘텐츠로 리디렉션 (및 표시)
brichins

21
또는 window.open(<url>, '_blank');Content-Disposition 헤더에 관계없이 다운로드가 현재 브라우저 컨텐츠를 대체하지 않도록하는 데 사용 하십시오.
Christopher King

4
이 솔루션의 문제점은 조작이 실패하거나 서버가 오류를 리턴하면 페이지가 오류 페이지로 경로 재 지정된다는 것입니다. 이를 해결하기 위해 iFrame 솔루션
kofifus

4
이 솔루션의 실제 문제-질문은 POST요청 에 관한 것 입니다.
Atomosk

35

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
a.click()... 파이어 폭스에서 작업에 어떤 생각을하지 않는 것?
bigpony

일부 브라우저에서는 a이 코드가 작동 및 / 또는 revokeObjectURL부분을 제거하기 위해 DOM에 dom 을 추가해야 할 수도 있습니다 .document.body.appendChild(a)
bigpony

내 하루를 구했다 (그리고 아마도 직업도 :)) 자바 스크립트 전문가가 아닙니다. 그러나 왜 간단한 "createObjectURL (new Blob ([atob (base64)]))"가 작동하지 않는지 모르겠습니다! 모든 본능이 반드시 말해야하는 것은 아닙니다. grrr ...
apil.tamang

var bytechars = atob(base64)에 오류가 발생 JavaScript runtime error: InvalidCharacterError합니다. Chrome 버전 75.0.3770.142를 사용하고 있지만 여기에서 무엇이 잘못되었는지 모르겠습니다.
Muflix

27

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>

2
선생님, 입력 : "Content-Disposition", "inline; .... 가난한 코더의 날을 구했습니다 :)
Vedran Maricevic.

1
이것은 "window.open"(댓글 중 하나가 언급)을 언급 한 유일한 대답입니다.
Andrew Koster

매개 변수가 많으면 too long url오류가 발생 하기 때문에 작동하지 않습니다 .
Muflix

25

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

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

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


3
감사합니다.이 솔루션을 사용했습니다. 매력처럼 일했다. 또한 응답에서 얼룩을 얻지 못하면 새 얼룩을 만드십시오.
fabio.sang이

6
IE 처리 링크가
startsWith_R

당신이 IE11 작업하는 경우 @startsWith_R에서 링크는 정말 도움이
alexventuraio

고맙습니다.
Zaki Mohammed

23

해결 방법 솔루션으로 거의 기능을 만들지 않았습니다 (@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');
});

그것은 매우 이상한 방식으로 서버에 데이터를 보냅니다 . 호환되는 POST를 작성하도록 변경할 수 있는지 궁금합니다.
Shayne

16

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

" 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 이상으로 조정했습니다. 파일 형식에 따라 설정해야합니다. 이 기술을 사용하여 모든 종류의 파일을 다운로드 할 수 있습니다.


"Ajax를 통해 파일을 다운로드 할 수 없으며 XMLHttpRequest를 사용해야합니다." XMLHttpRequest는 정의에 따라 AJAX입니다. 그렇지 않으면 최신 웹 브라우저를위한 훌륭한 솔루션입니다. 를 지원하지 않는 IE의 HTMLAnchorElement.download경우 독점 msSaveOrOpenBlob 메서드 와 결합 하려고 합니다.
Tsahi Asher

15

좋아, ndpu의 코드를 바탕으로 ajax_download의 개선 된 버전이라고 생각합니다.

function ajax_download(url, data) {
    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 +"'>" 

    Object.keys(data).forEach(function(key){
        iframe_html += "<input type='hidden' name='"+key+"' value='"+data[key]+"'>";

    });

        iframe_html +="</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});
});

매개 변수는 이전 예에 따라 json 인코딩 문자열이 아닌 입력에서 오는 것처럼 적절한 포스트 매개 변수로 전송됩니다.

주의 사항 : 해당 양식에 가변 주입 가능성에주의하십시오. 해당 변수를 인코딩하는 더 안전한 방법이있을 수 있습니다. 또는 탈출을 고려하십시오.


이것이 작동하는 예입니다. 감사. iframe없이 window.location없이 가능합니까?
Marek Bar

숨겨진 양식을 DOM 맨 아래에 추가 할 수 있다고 가정합니다. 또한 탐색 할 가치가있는 것은 Shadow dom을 사용하는 것이지만 이전 브라우저에서는 반드시 잘 지원되지는 않습니다.
Shayne

이 코드 에서이 오류가 발생합니다. Uncaught SecurityError: Blocked a frame with origin "http://foo.bar.com" from accessing a frame with origin "null". The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "data". Protocols must match.
void

이 양식을 일부 모델 클래스에 매핑하려면 어떻게해야합니까? 나는 : @ResourceMapping() public void downloadFile(final ResourceRequest request, final ResourceResponse response, @ModelAttribute("downForm") FormModel model) 하지만 작동하지 않습니다 ..
bartex9

void : 일종의 교차 출처 보안 문제 일 수 있습니다. 아마도 전체 스택 오버플로 질문과 그 자체입니다. @ bartex9 : 사용중인 프레임 워크의 종류에 크게 의존합니다. 그러나 원칙적으로 파일 자체를 파일 시스템의 웹 액세스 가능 영역 또는 고 가용성을위한 Amazon S3와 같은 파일로 푸시하면서 이름과 경로를 지정하여 저장하는 것이
Shayne

8

여기 내가 한 일, 순수한 자바 스크립트와 HTML이 있습니다. 테스트하지는 않았지만 모든 브라우저에서 작동합니다.

자바 스크립트 기능

var iframe = document.createElement('iframe');
iframe.id = "IFRAMEID";
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.src = 'SERVERURL'+'?' + $.param($scope.filtro);
iframe.addEventListener("load", function () {
     console.log("FILE LOAD DONE.. Download should start now");
});

모든 브라우저에서 지원되는 구성 요소 만 사용하면 추가 라이브러리가 없습니다.

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

다음은 서버 측 JAVA Spring 컨트롤러 코드입니다.

@RequestMapping(value = "/rootto/my/xlsx", method = RequestMethod.GET)
public void downloadExcelFile(@RequestParam(value = "param1", required = false) String param1,
    HttpServletRequest request, HttpServletResponse response)
            throws ParseException {

    Workbook wb = service.getWorkbook(param1);
    if (wb != null) {
        try {
            String fileName = "myfile_" + sdf.format(new Date());
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + ".xlsx\"");
            wb.write(response.getOutputStream());
            response.getOutputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    }

그것은 당신을 위해 작동한다면 (iframe에 아무것도로드되지 않기 때문에) 컨텐츠 처리 첨부 파일 컨텐츠에 대해로드 이벤트가 호출되지 않은 것 같습니다 (console.log를 얻음) pls는 샘플을 게시합니다
kofifus

다음은 빠른 바이올린 jsfiddle.net/y2xezyoj입니다 . pdf 파일이 iframe에로드되는 즉시로드 이벤트를 발생시킵니다. -disposition "," attachment ; filename = \ ""+ fileName + ".xlsx \" ");"
manukyanv07 2016 년

1
네, 그것은 그 경우에 작동하지만 파일이 다운로드되는 경우입니다 서버는 내용 - 처리 전송 : 첨부 파일을 다음로드 이벤트 내 지점이었다하지 않습니다 화재
kofifus

서버가 처리를 완료 한 직후 파일 전송이 시작되면 바로로드 이벤트가 시작됩니다. 이것은 내가 찾고있는 것입니다, 1- 버튼을 차단하고 처리를 보여 주어 사용자가 상황에 대한 피드백을 가질 수 있도록하십시오. 2-그런 다음 서버가 처리를 마치고 파일을 보내려고 할 때 버튼을 잠금 해제하고 처리 스피너 4를 제거하는 3- (로드 이벤트가 발생합니다)-사용자가 파일 저장으로 팝업되거나 브라우저에서 다운로드를 시작합니다. 정의 된 다운로드 위치 내 영어 죄송합니다.
manukyanv07

5
function downloadURI(uri, name) 
{
    var link = document.createElement("a");
    link.download = name;
    link.href = uri;
    link.click();
}

답을 설명해 주시겠습니까? 그것은 당신이 한 일을 다른 사람들이 이해하도록 도와 주므로 당신의 기술을 그들의 상황에 적용 할 수 있습니다.
Wai Ha Lee

2
경고 : Safari와 IE는이 download속성을 지원하지 않으므로 파일의 이름은 "알 수 없음"입니다
Yangshun Tay

4

Rails에서는 다음과 같이합니다.

function download_file(file_id) {
  let url       = '/files/' + file_id + '/download_file';
    $.ajax({
    type: 'GET',
    url: url,
    processData: false,
    success: function (data) {
       window.location = url;
    },
    error: function (xhr) {
     console.log(' Error:  >>>> ' + JSON.stringify(xhr));
    }
   });
 }

트릭은 window.location 부분입니다. 컨트롤러의 방법은 다음과 같습니다.

# GET /files/{:id}/download_file/
def download_file
    send_file(@file.file,
          :disposition => 'attachment',
          :url_based_filename => false)
end

2
빠른 질문, 이것이 파일을 두 번 생성하지 않습니까? 일단 당신은 아약스 요청을 보냅니다. 그런 다음 페이지를 동일한 URL로 리디렉션합니다. 어떻게 제거 할 수 있습니까?
coderhs

내 경우에는 그렇지 않습니다. 그래도 Chrome에서만 테스트했습니다.
aarkerio 2016 년

코데가 이미 올바르게 표시되어 있으므로 작업이 두 번 호출됩니다.
Sven

나에게도 두 번 전화가왔다.
CSquared

4

https://developer.mozilla.org/en-US/docs/Web/API/Window/open 사용window.open

예를 들어이 코드 줄을 클릭 처리기에 넣을 수 있습니다.

window.open('/file.txt', '_blank');

'_blank'window-name 때문에 새 탭이 열리고 해당 탭에서 URL이 열립니다.

서버 측 코드에는 다음과 같은 내용이 있어야합니다.

res.set('Content-Disposition', 'attachment; filename=file.txt');

그렇게하면 브라우저는 파일을 표시하는 대신 파일을 디스크에 저장하라는 메시지를 표시해야합니다. 또한 방금 연 탭을 자동으로 닫습니다.


4

CSV 파일을 다운로드 한 다음 다운로드가 완료된 후 무언가를 시도합니다. 따라서 적절한 callback기능 을 구현해야 합니다.

window.location="..."다운로드가 끝난 후 프로그램을 조작 할 수 없기 때문에 사용하는 것은 좋지 않습니다. 이런 식으로 헤더를 변경하면 좋은 생각이 아닙니다.

fetch좋은 대안이지만 IE 11을 지원할 수 없습니다 . 그리고 window.URL.createObjectURLIE 11.You가 참조 할 수 있습니다 지원할 수없는 .

이것은 내 코드이며 Shahrukh Alam의 코드와 유사합니다. 그러나 window.URL.createObjectURL메모리 누수 가 발생할 수 있으므로주의해야합니다 . 당신은 이것을 참조 할 수 있습니다 . 응답이 도착하면 데이터는 브라우저의 메모리에 저장됩니다. 따라서 a링크 를 클릭하기 전에 파일이 다운로드되었습니다. 다운로드 후 무엇이든 할 수 있음을 의미합니다.

$.ajax({
    url: 'your download url',
    type: 'GET',
}).done(function (data, textStatus, request) {
    // csv => Blob
    var blob = new Blob([data]);

    // the file name from server.
    var fileName = request.getResponseHeader('fileName');

    if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
    window.navigator.msSaveOrOpenBlob(blob, fileName);
    } else { // for others
    var url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    a.download = fileName;
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);

    //Do something after download 
    ...

    }
}).then(after_download)
}

4

AJAX에서 파일을받은 후 다운로드하는 방법

파일이 오랫동안 생성되어 PRELOADER를 표시해야 할 때 편리합니다.

웹 양식을 제출할 때의 예 :

<script>
$(function () {
    $('form').submit(function () {
        $('#loader').show();
        $.ajax({
            url: $(this).attr('action'),
            data: $(this).serialize(),
            dataType: 'binary',
            xhrFields: {
                'responseType': 'blob'
            },
            success: function(data, status, xhr) {
                $('#loader').hide();
                // if(data.type.indexOf('text/html') != -1){//If instead of a file you get an error page
                //     var reader = new FileReader();
                //     reader.readAsText(data);
                //     reader.onload = function() {alert(reader.result);};
                //     return;
                // }
                var link = document.createElement('a'),
                    filename = 'file.xlsx';
                // if(xhr.getResponseHeader('Content-Disposition')){//filename 
                //     filename = xhr.getResponseHeader('Content-Disposition');
                //     filename=filename.match(/filename="(.*?)"/)[1];
                //     filename=decodeURIComponent(escape(filename));
                // }
                link.href = URL.createObjectURL(data);
                link.download = filename;
                link.click();
            }
        });
        return false;
    });
});
</script>

옵션 기능은 예제를 단순화하기 위해 주석 처리됩니다.

서버에서 임시 파일을 만들 필요가 없습니다.

jQuery v2.2.4에서 OK. 이전 버전에는 오류가 있습니다.

Uncaught DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').

Content-Disposition에서 파일 이름을 얻으려면이 일치가 작동했습니다 filename.match(/filename=(.*)/)[1](큰 따옴표 또는 물음표 제외) -regex101.com/r/2AsD4y/2 . 그러나 솔루션은 많이 검색 한 후에 작동 한 유일한 솔루션이었습니다.
jstuardo

3

파일을 다운로드하기 위해 위의 답변에 몇 가지 추가

아래는 바이트 배열을 생성하는 Java 스프링 코드입니다.

@RequestMapping(value = "/downloadReport", method = { RequestMethod.POST })
    public ResponseEntity<byte[]> downloadReport(
            @RequestBody final SomeObejct obj, HttpServletResponse response) throws Exception {

        OutputStream out = new ByteArrayOutputStream();
        // write something to output stream
        HttpHeaders respHeaders = new HttpHeaders();
        respHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        respHeaders.add("X-File-Name", name);
        ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
        return new ResponseEntity<byte[]>(bos.toByteArray(), respHeaders, HttpStatus.CREATED);
    }

이제 FileSaver.js를 사용하는 자바 스크립트 코드에서 아래 코드가있는 파일을 다운로드 할 수 있습니다

var json=angular.toJson("somejsobject");
var url=apiEndPoint+'some url';
var xhr = new XMLHttpRequest();
//headers('X-File-Name')
xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 201) {
        var res = this.response;
        var fileName=this.getResponseHeader('X-File-Name');
        var data = new Blob([res]);
        saveAs(data, fileName); //this from FileSaver.js
    }
}    
xhr.open('POST', url);
xhr.setRequestHeader('Authorization','Bearer ' + token);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'arraybuffer';
xhr.send(json);

위의 파일을 다운로드합니다


2

여기 MVC를 사용할 때 작동 코드가 있으며 컨트롤러에서 파일을 가져옵니다.

바이트 배열을 선언하고 채운다 고 가정하면 File 함수 (System.Web.Mvc 사용)를 사용하면됩니다.

byte[] bytes = .... insert your bytes in the array
return File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, "nameoffile.exe");

그런 다음 동일한 컨트롤러에서 그 두 가지 기능을 추가하십시오.

protected override void OnResultExecuting(ResultExecutingContext context)
    {
        CheckAndHandleFileResult(context);

        base.OnResultExecuting(context);
    }

    private const string FILE_DOWNLOAD_COOKIE_NAME = "fileDownload";

    /// <summary>
    /// If the current response is a FileResult (an MVC base class for files) then write a
    /// cookie to inform jquery.fileDownload that a successful file download has occured
    /// </summary>
    /// <param name="context"></param>
    private void CheckAndHandleFileResult(ResultExecutingContext context)
    {
        if (context.Result is FileResult)
            //jquery.fileDownload uses this cookie to determine that a file download has completed successfully
            Response.SetCookie(new HttpCookie(FILE_DOWNLOAD_COOKIE_NAME, "true") { Path = "/" });
        else
            //ensure that the cookie is removed in case someone did a file download without using jquery.fileDownload
            if (Request.Cookies[FILE_DOWNLOAD_COOKIE_NAME] != null)
                Response.Cookies[FILE_DOWNLOAD_COOKIE_NAME].Expires = DateTime.Now.AddYears(-1);
    }

그런 다음 컨트롤러를 호출하여 "성공"또는 "실패"콜백을 다운로드하여받을 수 있습니다.

$.fileDownload(mvcUrl('name of the controller'), {
            httpMethod: 'POST',
            successCallback: function (url) {
            //insert success code

            },
            failCallback: function (html, url) {
            //insert fail code
            }
        });

1

실제로 ajax를 사용하지 않는 동안 자바 스크립트 호출을 사용하여 다운로드를 요청한 다음 다운로드가 실제로 시작될 때 콜백을 얻을 수있는 수정 사항을 발견했습니다. 링크가 파일을 보내기 전에 파일을 작성하는 데 약간의 시간이 걸리는 서버 측 스크립트를 실행하는 경우 유용합니다. 처리 중임을 알리고 파일을 마지막으로 보내면 처리 알림을 제거합니다. 그렇기 때문에 파일을 요청할 때 이벤트가 발생하고 실제로 다운로드가 시작될 때 이벤트가 발생할 수 있도록 ajax를 통해 파일을로드하려고했습니다.

첫 페이지의 js

function expdone()
{
    document.getElementById('exportdiv').style.display='none';
}
function expgo()
{
   document.getElementById('exportdiv').style.display='block';
   document.getElementById('exportif').src='test2.php?arguments=data';
}

iframe

<div id="exportdiv" style="display:none;">
<img src="loader.gif"><br><h1>Generating Report</h1>
<iframe id="exportif" src="" style="width: 1px;height: 1px; border:0px;"></iframe>
</div>

그런 다음 다른 파일 :

<!DOCTYPE html>
<html>
<head>
<script>
function expdone()
{
    window.parent.expdone();
}
</script>
</head>
<body>
<iframe id="exportif" src="<?php echo "http://10.192.37.211/npdtracker/exportthismonth.php?arguments=".$_GET["arguments"]; ?>"></iframe>
<script>document.getElementById('exportif').onload= expdone;</script>
</body></html>

js를 사용하여 데이터 가져 오기를 읽는 방법이 있으므로 PHP가 필요하지 않습니다. 그러나 나는 그것을 손으로 알지 못하고 내가 사용하는 서버는 PHP를 지원하므로 이것이 나를 위해 작동합니다. 누군가를 도울 수 있도록 공유하겠다고 생각했습니다.


0

jQuery File Download를 사용하려면 IE에 대해이 점에 유의하십시오. 응답을 재설정해야합니다. 그렇지 않으면 다운로드되지 않습니다

    //The IE will only work if you reset response
    getServletResponse().reset();
    //The jquery.fileDownload needs a cookie be set
    getServletResponse().setHeader("Set-Cookie", "fileDownload=true; path=/");
    //Do the reset of your action create InputStream and return

당신의 행동은 ServletResponseAware 접근을 구현할 수 있습니다getServletResponse()


0

Ajax 호출을 통해 할 수 없다는 것이 확실합니다.

그러나 해결 방법이 있습니다.

단계 :

파일을 다운로드하기 위해 form.submit ()을 사용하는 경우 수행 할 수있는 작업은 다음과 같습니다.

  1. 클라이언트에서 서버로의 Ajax 호출을 작성하고 파일 스트림을 세션 내에 저장하십시오.
  2. 서버에서 "성공"이 리턴되면 form.submit ()을 호출하여 세션에 저장된 파일 스트림을 스트리밍하십시오.

form.submit ()을 만든 후 파일을 다운로드해야하는지 여부를 결정하려는 경우에 유용합니다. 예 : form.submit ()에서 서버 측에서 예외가 발생하는 경우가있을 수 있습니다. 충돌이 발생하면 클라이언트 측에 사용자 정의 메시지를 표시해야 할 수도 있습니다. 이러한 경우이 구현이 도움이 될 수 있습니다.


0

ajax로 웹 페이지를 다운로드하는 또 다른 솔루션이 있습니다. 그러나 먼저 처리하고 다운로드 해야하는 페이지를 언급하고 있습니다.

먼저 페이지 처리를 결과 다운로드와 분리해야합니다.

1) 페이지 계산 만 아약스 호출에서 이루어집니다.

$ .post ( "CalculusPage.php", {calculusFunction : true, ID : 29, data1 : "a", data2 : "b"},

       기능 (데이터, 상태) 
       {
            if (상태 == "성공") 
            {
                / * 2) 답변에서 이전 계산을 사용하는 페이지가 다운로드됩니다. 예를 들어, 이것은 ajax 호출에서 계산 된 테이블의 결과를 인쇄하는 페이지 일 수 있습니다. * /
                window.location.href = DownloadPage.php + "? ID ="+ 29;
            }               
       }
);

// 예를 들어 CalculusPage.php에서

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

        $ query = "ExamplePage에 삽입 (data1, data2) 값 ( '". $ _ POST [ "data1"]. "', '". $ _ POST [ "data2"]. "') WHERE id =". $ ID;
        ...
    }

// 예 : DownloadPage.php에서

    $ ID = $ _GET [ "ID"];

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

    $ filename = "Export_Data.xls";
    header ( "Content-Type : application / vnd.ms-excel");
    header ( "콘텐츠-처리 : 인라인; 파일 이름 = $ 파일 이름");

    ...

이 솔루션이 저와 마찬가지로 많은 사람들에게 유용 할 수 있기를 바랍니다.


0
The HTML Code:-

'<button type="button" id="GetFile">Get File!</button>'


The jQuery Code:-

'$('#GetFile').on('click', function () {
    $.ajax({
        url: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/172905/test.pdf',
        method: 'GET',
        xhrFields: {
            responseType: 'blob'
        },
        success: function (data) {
            var a = document.createElement('a');
            var url = window.URL.createObjectURL(data);
            a.href = url;
            a.download = 'myfile.pdf';
            document.body.append(a);
            a.click();
            a.remove();
            window.URL.revokeObjectURL(url);
        }
    });
});'

코드에만 답변이 있어야 코드가 작동하는 방식과 질문에 답변하는 이유를 설명하는 최소한의 설명이 있어야합니다.
로베르토 카 보니

0

그것은 모든 브라우저에서 잘 작동합니다 (asp.net 코어를 사용하고 있습니다)

            function onDownload() {

  const api = '@Url.Action("myaction", "mycontroller")'; 
  var form = new FormData(document.getElementById('form1'));

  fetch(api, { body: form, method: "POST"})
      .then(resp => resp.blob())
      .then(blob => {
          const url = window.URL.createObjectURL(blob);
        $('#linkdownload').attr('download', 'Attachement.zip');
          $('#linkdownload').attr("href", url);
          $('#linkdownload')
              .fadeIn(3000,
                  function() { });

      })
      .catch(() => alert('An error occurred'));



}
 
 <button type="button" onclick="onDownload()" class="btn btn-primary btn-sm">Click to Process Files</button>
 
 
 
 <a role="button" href="#" style="display: none" class="btn btn-sm btn-secondary" id="linkdownload">Click to download Attachments</a>
 
 
 <form asp-controller="mycontroller" asp-action="myaction" id="form1"></form>
 
 

        function onDownload() {
            const api = '@Url.Action("myaction", "mycontroller")'; 
            //form1 is your id form, and to get data content of form
            var form = new FormData(document.getElementById('form1'));

            fetch(api, { body: form, method: "POST"})
                .then(resp => resp.blob())
                .then(blob => {
                    const url = window.URL.createObjectURL(blob);
                    $('#linkdownload').attr('download', 'Attachments.zip');
                    $('#linkdownload').attr("href", url);
                    $('#linkdownload')
                        .fadeIn(3000,
                            function() {

                            });
                })
                .catch(() => alert('An error occurred'));                 

        }

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.