ajax 요청을 사용하여 파일 다운로드


95

버튼을 클릭 할 때 "ajax 다운로드 요청"을 보내려고하므로 다음과 같이 시도했습니다.

자바 스크립트 :

var xhr = new XMLHttpRequest();
xhr.open("GET", "download.php");
xhr.send();

download.php :

<?
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename= file.txt");
header("Content-Transfer-Encoding: binary");    
readfile("file.txt");
?>

하지만 예상대로 작동하지 않습니다. 어떻게해야합니까? 미리 감사드립니다


2
작동하지 않습니다. [이 질문] [1]을 참조하십시오. [1] : stackoverflow.com/questions/8771342/…
olegsv

2
Dolocation.href='download.php';
Musa

답변:


92

업데이트 2015 년 4 월 27 일

HTML5 장면에 등장하는 것은 다운로드 속성 입니다. Firefox 및 Chrome 에서 지원 되며 곧 IE11에 제공 될 예정입니다. 필요에 따라 window.location다운로드하려는 파일이 사이트와 동일한 출처에있는 한 AJAX 요청 (또는 사용 ) 대신 사용할 수 있습니다.

지원 여부를 테스트하기 위해 일부 JavaScriptwindow.location사용 하고 그렇지 않은 경우 호출로 전환 하여 AJAX 요청 / 대체를 항상 만들 수 있습니다.downloadwindow.location

원래 답변

다운로드를 요청하기 위해 물리적으로 파일로 이동해야하므로 AJAX 요청으로 다운로드 프롬프트를 열 수 없습니다. 대신 성공 함수를 사용하여 download.php로 이동할 수 있습니다. 다운로드 프롬프트가 열리지 만 현재 페이지는 변경되지 않습니다.

$.ajax({
    url: 'download.php',
    type: 'POST',
    success: function() {
        window.location = 'download.php';
    }
});

이것이 질문에 대한 답이지만 window.locationAJAX 요청을 완전히 사용 하고 피하는 것이 좋습니다 .


39
이 링크를 두 번 호출하지 않습니까? 나는 비슷한 배를 타고있다 ... 헤더에 많은 보안 정보를 전달하고 있고 성공 함수에서 파일 객체를 구문 분석 할 수 있지만 다운로드 프롬프트를 트리거하는 방법을 모릅니다.
user1447679 2015

3
페이지를 두 번 호출하므로 해당 페이지에서 데이터베이스를 쿼리하는 경우 DB로 두 번 이동합니다.
mmmmmm

1
@ user1447679 대체 솔루션 참조 : stackoverflow.com/questions/38665947/…
John

1
이것이 어떻게 도움이되었는지 설명하겠습니다. 예제가 더 완벽 할 수있었습니다. "download.php? get_file = true"또는 뭔가 ... 양식 제출에 대한 오류 검사를 수행 한 다음 csv 파일을 생성하는 ajax 함수가 있습니다. 오류 검사가 실패하면 실패한 이유를 다시 확인해야합니다. CSV를 생성하면 부모에게 "계속해서 파일을 가져 오십시오"라고 알리는 것입니다. 양식 변수를 사용하여 ajax 파일에 게시 한 다음 동일한 파일에 "방금 만든 파일을 가져다주세요"라는 다른 매개 변수를 게시하여이를 수행합니다 (경로 / 이름은 ajax 파일에 하드 코딩 됨).
Scott

4
그러나 요청을 2 회를 보낼 것입니다, 그것은 적절하지 않다
Dharmendrasinh Chudasama

44

브라우저가 파일을 다운로드하도록하려면 다음과 같이 요청해야합니다.

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

7
이것은 나를 위해 작동하지만 firefox에서는 먼저 DOM에 <a> 태그를 넣고 파일이 자동으로 다운로드되도록 즉석에서 생성하는 대신 내 링크로 참조해야했습니다.
Erik Donohoo

작동하지만 실행 중에 파일이 생성되면 어떻게됩니까? 파일이 이미 생성되었을 때처럼 작동하지 않습니다.
Diego

@Taha Edge에서 이것을 테스트했는데 작동하는 것 같았습니다. 하지만 IE에 대해서는 모릅니다. 내 클라이언트가 IE 사용자를 대상으로하지 않습니다 ;-) 운이 좋은가요? : D
Sнаđошƒаӽ apr

1
@ErikDonohoo <a>JS로 "즉석에서"태그를 만들 수도 있지만 document.body. 동시에 숨기고 싶을 것입니다.
Márton Tamás

@ João에게 감사드립니다. 저는 오랫동안이 솔루션을 찾고있었습니다.
Abhishek Gharai

43

실제로 이것을 위해 아약스가 전혀 필요하지 않습니다. 버튼의 href로 "download.php"를 설정하거나 링크가 아닌 경우 다음을 사용하십시오.

window.location = 'download.php';

브라우저는 바이너리 다운로드를 인식하고 실제 페이지를로드하지 않고 파일을 다운로드로 제공해야합니다.


3
변경하는 데 사용하는 프로그래밍 언어 window.location JavaScript입니다.
mikemaccana 2014

1
@mikemaccana 맞습니다. 실제로 ajax를 의미했습니다. :).
Jelle Kralt 2014

2
솔루션을 위해 높고 낮게 사냥 해 왔으며 이것은 매우 우아하고 완벽합니다. 정말 고맙습니다.
Yangshun Tay

2
물론이 솔루션은 이미 존재하는 정적 파일 인 경우에만 작동합니다.
krillgar

1
서버가 오류로 응답하는 경우 브라우저에서 오류 페이지로 리디렉션되지 않고 기본 페이지에 머무를 방법이 없습니다. 적어도 이것은 window.location의 결과가 404를 반환 할 때 Chrome이 수행하는 작업입니다.
Ian

20

Chrome, Firefox, Edge, IE11에서 테스트 된 크로스 브라우저 솔루션.

DOM에서 숨겨진 링크 태그를 추가합니다.

<a id="target" style="display: none"></a>

그때:

var req = new XMLHttpRequest();
req.open("GET", downloadUrl, true);
req.responseType = "blob";
req.setRequestHeader('my-custom-header', 'custom-value'); // adding some headers (if needed)

req.onload = function (event) {
  var blob = req.response;
  var fileName = null;
  var contentType = req.getResponseHeader("content-type");

  // IE/EDGE seems not returning some response header
  if (req.getResponseHeader("content-disposition")) {
    var contentDisposition = req.getResponseHeader("content-disposition");
    fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1);
  } else {
    fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1);
  }

  if (window.navigator.msSaveOrOpenBlob) {
    // Internet Explorer
    window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName);
  } else {
    var el = document.getElementById("target");
    el.href = window.URL.createObjectURL(blob);
    el.download = fileName;
    el.click();
  }
};
req.send();

좋은 일반 코드. @leo와 같은 사용자 지정 헤더를 추가하여이 코드를 개선 할 수 Authorization있습니까?
vibs2006

1
감사합니다 @leo. 도움이됩니다. 또한 window.URL.revokeObjectURL(el.href);뒤에 el.click()무엇을 추가 할 것을 제안 합니까?
vibs2006

콘텐츠 처리가 UTF8이 아닌 파일 이름을 지정하면 파일 이름이 잘못됩니다.
Mathew Alden

14

것이 가능하다. 예를 들어 .csv 파일이 생성 된 직후와 같이 ajax 함수 내에서 다운로드를 시작할 수 있습니다.

연락처 데이터베이스를 .csv 파일로 내보내는 ajax 기능이 있으며 완료 직후 자동으로 .csv 파일 다운로드를 시작합니다. 따라서 responseText를 받고 모든 것이 정상이면 다음과 같이 브라우저를 리디렉션합니다.

window.location="download.php?filename=export.csv";

download.php 파일은 다음과 같습니다.

<?php

    $file = $_GET['filename'];

    header("Cache-Control: public");
    header("Content-Description: File Transfer");
    header("Content-Disposition: attachment; filename=".$file."");
    header("Content-Transfer-Encoding: binary");
    header("Content-Type: binary/octet-stream");
    readfile($file);

?>

페이지 새로 고침이 전혀 없으며 파일이 자동으로 다운로드되기 시작합니다.

참고 -다음 브라우저에서 테스트되었습니다.

Chrome v37.0.2062.120 
Firefox v32.0.1
Opera v12.17
Internet Explorer v11

1
보안 상 위험하지 않습니까?
Mickael Bergeron Néron

트윗 담아 가기
페드로 수사

5
누구나 download.php? filename = [something]을 호출하고 경로와 파일 이름, 특히 일반적인 이름을 시도 할 수 있기 때문에 그렇게 생각할 수 있습니다. 이것은 프로그램이나 스크립트 내의 루프 내에서도 수행 될 수 있습니다.
Mickael Bergeron Néron

.htaccess가 그것을 피하지 않을까요?
Pedro Sousa

9
@PedroSousa .. 아니. htaccess는 Apache를 통해 파일 구조에 대한 액세스를 제어합니다. 액세스가 PHP 스크립트에 도달 했으므로 이제 htaccess가 해당 작업을 중지합니다. 실제로 PHP (및 실행중인 사용자)가 읽을 수있는 모든 파일을 readfile로 전달할 수 있기 때문에 이것은 매우 보안상의 허점입니다. 읽기 위해 요청 된 파일을 항상 삭제해야합니다
Prof


0

헤더에서 파일 이름을 디코딩하는 것은 조금 더 복잡합니다.

    var filename = "default.pdf";
    var disposition = req.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, '');
    }

3
전체 코드 블록의 형식을 지정하고 향후 독자의 이익을 위해 프로세스에 대한 추가 설명을 제공하십시오.
mickmackusa

0

이 솔루션은 위의 솔루션과 크게 다르지 않지만 저에게는 매우 잘 작동하고 깨끗하다고 ​​생각합니다.

파일 서버 측 (PHP를 사용하는 경우 base64_encode ())을 base64로 인코딩하고 base64로 인코딩 된 데이터를 클라이언트에 보내는 것이 좋습니다.

클라이언트에서 다음을 수행합니다.

 let blob = this.dataURItoBlob(THE_MIME_TYPE + "," + response.file);
 let uri = URL.createObjectURL(blob);
 let link = document.createElement("a");
 link.download = THE_FILE_NAME,
 link.href = uri;
 document.body.appendChild(link);
 link.click();
 document.body.removeChild(link);

이 코드는 인코딩 된 데이터를 링크에 넣고 링크 클릭을 시뮬레이션 한 다음 제거합니다.


a 태그를 숨기고 href를 동적으로 채울 수 있습니다. 추가 및 제거 할 필요 없음
BPeela

0

귀하의 요구 사항은 다음에서 다루어집니다. window.location('download.php');
하지만 항상 동일한 파일을 다운로드하는 것이 아니라 다운로드 할 파일을 전달해야한다고 생각합니다. 이것이 요청을 사용하는 이유입니다. 한 가지 옵션은 showfile.php와 같이 간단한 php 파일을 생성하는 것입니다. 같은 요청을

var myfile = filetodownload.txt
var url = "shofile.php?file=" + myfile ;
ajaxRequest.open("GET", url, true);

showfile.php

<?php
$file = $_GET["file"] 
echo $file;

여기서 file은 요청에서 Get 또는 Post를 통해 전달 된 파일 이름이며 함수에서 응답을 간단히 포착합니다.

if(ajaxRequest.readyState == 4){
                        var file = ajaxRequest.responseText;
                        window.location = 'downfile.php?file=' + file;  
                    }
                }

0

ajax에서 웹 페이지를 다운로드하는 또 다른 솔루션이 있습니다. 하지만 먼저 처리 한 다음 다운로드해야하는 페이지를 언급하고 있습니다.

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

1) 페이지 계산은 ajax 호출에서만 수행됩니다.

$ .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 = "INSERT INTO ExamplePage (data1, data2) VALUES ( '". $ _ POST [ "data1"]. "', '". $ _ POST [ "data2"]. "') WHERE id =". $ ID;
        ...
    }

// 예 : DownloadPage.php

    $ ID = $ _GET [ "ID"];

    $ sede = "선택 * FROM ExamplePage WHERE id =". $ ID;
    ...

    $ filename = "Export_Data.xls";
    header ( "콘텐츠 유형 : application / vnd.ms-excel");
    header ( "Content-Disposition : inline; filename = $ filename");

    ...

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


0

보다 현대적인 접근 방식을 찾는 사람들을 위해 fetch API. 다음 예는 스프레드 시트 파일을 다운로드하는 방법을 보여줍니다. 다음 코드로 쉽게 수행 할 수 있습니다.

fetch(url, {
    body: JSON.stringify(data),
    method: 'POST',
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    },
})
.then(response => response.blob())
.then(response => {
    const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
    const downloadUrl = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = downloadUrl;
    a.download = "file.xlsx";
    document.body.appendChild(a);
    a.click();
})

저는이 접근 방식이 다른 XMLHttpRequest솔루션 보다 이해하기 훨씬 쉽다고 생각합니다 . 또한 jQuery추가 라이브러리를 추가 할 필요없이 접근 방식 과 유사한 구문을 가지고 있습니다.

물론이 새로운 접근 방식은 IE에서 작동하지 않으므로 개발중인 브라우저를 확인하는 것이 좋습니다. 다음 링크 에서 전체 브라우저 호환성 목록을 찾을 수 있습니다 .

중요 :이 예에서는 주어진 .NET에서 수신하는 서버에 JSON 요청을 보냅니다 url. 이것은 url설정되어야합니다. 제 예에서는이 부분을 알고 있다고 가정합니다. 또한 요청이 작동하는 데 필요한 헤더를 고려하십시오. JSON을 전송하고 있으므로 Content-Type헤더 를 추가 하고로 설정 application/json; charset=utf-8하여 서버가 수신 할 요청 유형을 알려야합니다 .


0

@Joao Marcos 솔루션이 나를 위해 작동하지만 코드가 어떻게 보이는지 아래에서 IE에서 작동하도록 코드를 수정해야했습니다.

       downloadFile(url,filename) {
        var that = this;
        const extension =  url.split('/').pop().split('?')[0].split('.').pop();

        var req = new XMLHttpRequest();
        req.open("GET", url, true);
        req.responseType = "blob";
        req.onload = function (event) {
            const fileName = `${filename}.${extension}`;
            const blob = req.response;

            if (window.navigator.msSaveBlob) { // IE
                window.navigator.msSaveOrOpenBlob(blob, fileName);
            } 
            const link = document.createElement('a');
            link.href = window.URL.createObjectURL(blob);                
            link.download = fileName;
            link.click();
            URL.revokeObjectURL(link.href);

        };

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