아약스 포스트에서 파일 다운로드 처리


392

특정 URL로 아약스 POST 요청을 보내는 자바 스크립트 앱이 있습니다. 응답은 JSON 문자열이거나 파일 (첨부 파일) 일 수 있습니다. 내 아약스 호출에서 Content-Type 및 Content-Disposition을 쉽게 감지 할 수 있지만 응답에 파일이 포함 된 것을 감지하면 클라이언트에게 다운로드를 제공하려면 어떻게해야합니까? 나는 여기에서 비슷한 스레드를 많이 읽었지만 그 중 어느 것도 내가 찾고있는 대답을 제공하지 않습니다.

이 옵션에 아약스를 사용해서는 안되거나 브라우저를 리디렉션해야한다는 제안을 게시하지 마십시오. 일반 HTML 양식을 사용하는 것도 옵션이 아닙니다. 필요한 것은 클라이언트에 다운로드 대화 상자를 표시하는 것입니다. 이 일을 어떻게 할 수 있습니까?


이 기사를 읽는 사람들을 위해이 게시물을 읽으십시오 : stackoverflow.com/questions/20830309/…
sobhan

질문에서 귀하의 솔루션을 제거했습니다. 아래의 답변 게시물로 게시해도 좋지만 질문 게시물에는 속하지 않습니다.
Martijn Pieters

답변:


111

양식을 작성하고 POST 메소드를 사용하여 양식을 제출하십시오. iframe이 필요하지 않습니다. 서버 페이지가 요청에 응답하면 파일의 MIME 유형에 대한 응답 헤더를 작성하면 다운로드 대화 상자가 표시됩니다. 여러 번 수행했습니다.

컨텐츠 유형의 응용 프로그램 / 다운로드를 원합니다. 사용중인 언어에 따라 다운로드를 제공하는 방법 만 검색하십시오.


35
질문에 명시된 바와 같이 : "일반 HTML 양식을 사용하는 것도 옵션이 아닙니다."
Pavle Predic

13
아니요. 일반 POST를 사용하면 브라우저가 POST URL로 이동하기 때문입니다. 페이지에서 벗어나고 싶지 않습니다. 백그라운드에서 요청을 수행하고 응답을 처리하여 클라이언트에 제시하고 싶습니다.
Pavle Predic

6
서버가 다른 답변과 같이 헤더를 다시 보내면 새 창에서 열립니다-이전에 해본 적이 있습니다. 서버 측 스크립트가 HTML 코드를 반환 한 경우에만 탐색 할 수 있습니다.

1
@PavlePredic JSON 텍스트 응답 또는 다운로드 파일 응답과 같은 두 가지 응답 시나리오를 관리하는 방법을 알아 내셨습니까?
웹 사용자

9
대답이 명확하지 않고 제안 된 솔루션이 작동하지 않습니다.
stack247

530

FileAPI의 일부를 사용하여 (현대 브라우저에서) 수행 할 수 있기 때문에 너무 빨리 포기하지 마십시오.

var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
    if (this.status === 200) {
        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;
        if (typeof File === 'function') {
            try {
                blob = new File([this.response], filename, { type: type });
            } catch (e) { /* Edge */ }
        }
        if (typeof blob === 'undefined') {
            blob = new Blob([this.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.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send($.param(params));

다음은 jQuery.ajax를 사용하는 이전 버전입니다. 응답이 일부 문자 집합의 문자열로 변환 될 때 이진 데이터를 엉망으로 만들 수 있습니다.

$.ajax({
    type: "POST",
    url: url,
    data: params,
    success: function(response, status, xhr) {
        // check for a filename
        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.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
});

1
고마워요! 그래도 HTTP 응답의 'Access-Control-Expose-Headers'와 'Access-Control-Allow-Headers'에 'Content-disposition'을 추가해야 작동했습니다.
JulienD

1
파일이 500MB보다 크면 작동하지 않습니다. 다른 API를 사용해야합니까?
hirra

URL뿐만 아니라 정리 부분의 DOM에서 요소를 제거하는 것은 어떻습니까? document.body.removeChild(a);
Scoregraphic September

@hirra 사용 responceType 대신 온로드 콜백 함수 그런 식으로 "arraybuffer"및 재 작성의 "덩어리", 그 var에 블롭 this.response (var에 블롭 this.response는 =;) 그래서var blob =this.responce; /** if (typeof File === 'function') { try { blob = new File([this.response], filename, { type: type }); } catch (e) { /* Edge */ } } if (typeof blob === 'undefined') { blob = new Blob([this.response], { type: type }); } */
크리스 Tobba

1
이것은 완벽한 솔루션입니다. 하나의 작은 변화. Typescript에서 필자 window.location.href = downloadUrl대신window.location = downloadUrl
michal.jakubeczy

39

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

" 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"링크에 첨부 한 다음 클릭합니다.

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

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

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


33

어떤 서버 측 언어를 사용하고 있습니까? 내 응용 프로그램에서 PHP의 응답에 올바른 헤더를 설정하여 AJAX 호출에서 파일을 쉽게 다운로드 할 수 있습니다.

서버 측 헤더 설정

header("HTTP/1.1 200 OK");
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

// The optional second 'replace' parameter indicates whether the header
// should replace a previous similar header, or add a second header of
// the same type. By default it will replace, but if you pass in FALSE
// as the second argument you can force multiple headers of the same type.
header("Cache-Control: private", false);

header("Content-type: " . $mimeType);

// $strFileName is, of course, the filename of the file being downloaded. 
// This won't have to be the same name as the actual file.
header("Content-Disposition: attachment; filename=\"{$strFileName}\""); 

header("Content-Transfer-Encoding: binary");
header("Content-Length: " . mb_strlen($strFile));

// $strFile is a binary representation of the file that is being downloaded.
echo $strFile;

이것은 실제로 다운로드 페이지로 브라우저를 '리디렉션'하지만 @ahren이 이미 언급했듯이 현재 페이지에서 벗어나지 않습니다.

올바른 헤더를 설정하는 것이 전부이므로 PHP가 아닌 경우 사용중인 서버 측 언어에 적합한 솔루션을 찾을 것입니다.

응답 클라이언트 측 처리

AJAX 호출 방법을 이미 알고 있다고 가정하면 클라이언트 측에서 서버에 대한 AJAX 요청을 실행합니다. 그런 다음 서버는이 파일을 다운로드 할 수있는 링크 (예 : 가리 키려는 '전달'URL)를 생성합니다. 예를 들어 서버는 다음과 같이 응답합니다.

{
    status: 1, // ok
    // unique one-time download token, not required of course
    message: 'http://yourwebsite.com/getdownload/ska08912dsa'
}

응답을 처리 할 때 iframe본문에 를 삽입하고 iframe의 SRC를 방금 수신 한 URL로 설정하십시오 (이 예제의 편의를 위해 jQuery 사용).

$("body").append("<iframe src='" + data.message +
  "' style='display: none;' ></iframe>");

위에 표시된대로 올바른 헤더를 설정 한 경우 iframe은 현재 페이지에서 브라우저를 탐색하지 않고 다운로드 대화 상자를 강제 실행합니다.

노트

귀하의 질문과 관련하여 추가 추가; AJAX 기술로 물건을 요청할 때 항상 JSON을 반환하는 것이 가장 좋습니다. JSON 응답을 수신 한 후 클라이언트 측에서 수행 할 작업을 결정할 수 있습니다. 예를 들어, 나중에 사용자가 다운로드를 직접 강요하는 대신 URL에 대한 다운로드 링크를 클릭하기를 원할 수도 있습니다. 현재 설정에서 클라이언트와 서버 측 모두를 업데이트해야합니다.


24

Angular 관점에서 솔루션을 찾고있는 사람들에게는 이것이 효과적입니다.

$http.post(
  'url',
  {},
  {responseType: 'arraybuffer'}
).then(function (response) {
  var headers = response.headers();
  var blob = new Blob([response.data],{type:headers['content-type']});
  var link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = "Filename";
  link.click();
});

도움이되었지만 원래 파일 이름을 유지해야합니다. "Content-Disposition"아래의 응답 헤더에 파일 이름이 표시되지만 코드의 응답 객체에서 해당 값을 찾을 수 없습니다. link.download = ""임의의 guid 파일 이름을 설정 하면 link.download = null"null"이라는 파일이 생성됩니다.
마리

@Marie, 업로드시 INPUT요소의 HTMLInputElement.files속성 을 사용하여 파일 이름을 기록 할 수 있습니다 . 자세한 내용 은 파일 입력에 대한 MDN 문서를 참조하십시오 .
팀 Hettler

얼룩 크기가 제한됩니다 : stackoverflow.com/questions/28307789/…
user0800

22

여기 내가 어떻게 작동하는지 https://stackoverflow.com/a/27563953/2845977

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

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

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


이것에 감사합니다, 나는 오늘 이것을 사용했습니다. 환상적인
라이언 윌슨

안녕하세요, jQuery 3.0이 필요합니까?
gbade_

또한 두 예제를 모두 빈 PDF 파일로 받고 있습니다. pdf 파일을 다운로드하려고합니다.
gbade_

@gbade_ 아니요, 특정 jQuery 버전이 필요하지 않습니다. 모든 버전에서 잘 작동합니다. 다운로드중인 PDF에 올바른 CORS 헤더가 있는지 확인 했습니까? 콘솔의 모든 오류는 디버그하는 데 도움이 될 수 있습니다
Mayur Padshala

이것은 download.js를 사용하여 나를 위해 일했습니다 :success: function (response, status, request) { download(response, "filename.txt", "application/text"); }
Sga

12

나는 당신이 이미 해결책을 찾았다는 것을 알지만, 큰 POST 요청으로 같은 것을 달성하려고 시도하는 데 도움이 될만한 정보를 추가하고 싶었습니다.

나는 실제로는 AJAX를 통해 "깨끗한"다운로드를 달성 할 수없는, 몇 주 전에 동일한 문제가 있었다, 필라멘트 그룹 정확하게 당신이 이미 발견했습니다 어떻게가 호출 될 때 작동하는 jQuery 플러그인 생성 jQuery를 파일 다운로드 그러나이 기술에 단점이있다.

AJAX (예 : 파일 + 1MB)를 통해 큰 요청을 보내는 경우 응답 성이 떨어집니다. 인터넷 연결 속도가 느리면 요청이 전송 될 때까지 많이 기다려야하며 파일이 다운로드 될 때까지 기다려야합니다. 즉각 "클릭"=> "팝업"=> "다운로드 시작"과는 다릅니다. "클릭"=> "데이터가 전송 될 때까지 기다리십시오"=> "응답을 기다리는 중"=> "다운로드 시작"과 비슷합니다. 요청이 전송 될 때까지 기다려야하므로 파일 크기가 두 배로 나타납니다. AJAX를 통해 다운로드 가능한 파일로 다시 가져옵니다.

1MB 미만의 작은 파일 크기로 작업하는 경우이를 알 수 없습니다. 그러나 내 앱에서 발견 한 것처럼 더 큰 파일 크기의 경우 거의 견딜 수 없습니다.

내 응용 프로그램을 사용하면 사용자가 동적으로 생성 된 이미지를 내보낼 수 있습니다.이 이미지는 POST 요청을 통해 base64 형식의 서버로 전송되며 (유일한 방법 임) 처리되고 .png, .jpg 파일, base64의 형태로 사용자에게 다시 전송됩니다. 이미지 + 1MB의 문자열이 너무 커서 사용자가 파일 다운로드를 시작하는 데 필요한 시간 이상을 기다려야합니다. 인터넷 연결 속도가 느리면 정말 성 가실 수 있습니다.

이것에 대한 나의 해결책은 파일을 서버에 임시로 작성하는 것이 었습니다. 일단 준비되면 "Please wait ..."와 "Download"상태 사이에서 동일하게 버튼 형태로 파일에 대한 링크를 동적으로 생성하십시오 시간이되면 미리보기 팝업 창에서 base64 이미지를 인쇄하여 사용자가 "오른쪽 클릭"하여 저장할 수 있습니다. 이것은 모든 대기 시간을 사용자가 더 견딜 수있게 만들고 속도를 높입니다.

2014 년 9 월 30 일 업데이트 :

이 글을 게시 한 지 몇 달이 지났고 마침내 큰 base64 문자열로 작업 할 때 속도를 높이는 더 좋은 방법을 찾았습니다. 이제 long64 텍스트 또는 longblog 필드를 사용하여 base64 문자열을 데이터베이스에 저장 한 다음 jQuery 파일 다운로드를 통해 레코드 ID를 전달합니다. 마지막으로 다운로드 스크립트 파일에서이 ID를 사용하여 데이터베이스를 쿼리하여 base64 문자열을 가져 와서 전달합니다. 다운로드 기능.

스크립트 다운로드 예 :

<?php
// Record ID
$downloadID = (int)$_POST['id'];
// Query Data (this example uses CodeIgniter)
$data       = $CI->MyQueries->GetDownload( $downloadID );
// base64 tags are replaced by [removed], so we strip them out
$base64     = base64_decode( preg_replace('#\[removed\]#', '', $data[0]->image) );
// This example is for base64 images
$imgsize    = getimagesize( $base64 );
// Set content headers
header('Content-Disposition: attachment; filename="my-file.png"');
header('Content-type: '.$imgsize['mime']);
// Force download
echo $base64;
?>

나는 이것이 OP가 요청한 것 이상의 방법이라는 것을 알고 있지만 내 결과로 내 대답을 업데이트하는 것이 좋을 것이라고 생각했습니다. 내 문제에 대한 해결책을 찾을 때, 내가 찾던 답을 얻지 못한 "AJAX POST 데이터에서 다운로드" 스레드를 많이 읽었습니다. 이 정보가 누군가가 이와 같은 것을 달성하는 데 도움이되기를 바랍니다.


jQuery File DownloadURL 만에 저를 리디렉션합니다. 나는 이것을 다음과 같이 부른다 jQuery.download("api/ide/download-this-file.php", {filePath: path2Down}, "POST");.
캐스퍼

5

수락 된 답변에 기술을 사용할 때 발생하는 몇 가지 어려움, 즉 양식 게시물을 사용하고 싶습니다.

  1. 요청에 헤더를 설정할 수 없습니다. 인증 스키마에 헤더가 포함 된 경우 Authorson 헤더에 전달 된 Json-Web-Token은 쿼리 매개 변수와 같은 다른 방법을 찾아야합니다.

  2. 요청이 언제 완료되었는지 알 수 없습니다. 글쎄, 당신은 응답에 따라 설정된 쿠키를 사용할 수 있습니다. jquery.fileDownload 에서 있지만 완벽하지는 않습니다. 동시 요청에는 작동하지 않으며 응답이 도착하지 않으면 중단됩니다.

  3. 서버가 오류로 응답하면 사용자는 오류 페이지로 리디렉션됩니다.

  4. 양식에서 지원하는 컨텐트 유형 만 사용할 수 있습니다 . 즉, JSON을 사용할 수 없습니다.

S3에 파일을 저장하고 미리 서명 된 URL을 보내 파일을 얻는 방법을 사용했습니다.


5

보다 현대적인 접근 방식을 원하는 사용자는을 사용할 수 있습니다 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에서 작동하지 않기 때문에 개발중인 브라우저를 확인하는 것이 좋습니다. 다음 [link] [1]에서 전체 브라우저 호환성 목록을 찾을 수 있습니다.

중요 :이 예제에서는 주어진 서버에서 수신 대기하는 JSON 요청을 서버에 보냅니다 url. 이것은 url내 예에 나는 당신이이 부분을 알고 가정하고, 설정해야합니다. 또한 요청이 작동하는 데 필요한 헤더를 고려하십시오. JSON을 보내고 있기 때문에 서버가 수신 할 요청 유형을 알 수 있도록 Content-Type헤더 를 추가 하고로 설정 해야합니다 application/json; charset=utf-8.


1
대박! 다운로드 팝업 대신 새 탭에서 열려면 :``const window = open (downloadUrl, "_blank"); if (window! == null) window.focus (); ```
앤디

여러 데이터 집합에 대해이 작업을 수행 할 수있는 방법이 있습니까? 예를 들어, api 호출에서 {fileOne : data, fileTwo : data, fileThree : data}를 가져 와서 한 번에 세 개의 다운로드 된 파일을 생성합니까? 감사!
il0v3d0g

흠, 나는 그것을 시도하지 않았습니다. 그러나 이미지는 항상 zip 파일로 압축하여 다운로드 할 수 있습니다. 가능한지 확인하겠습니다.
Alain Cruz

4

다음은 임시 숨겨진 양식을 사용하는 솔루션입니다.

//Create an hidden form
var form = $('<form>', {'method': 'POST', 'action': this.href}).hide();

//Add params
var params = { ...your params... };
$.each(params, function (k, v) {
    form.append($('<input>', {'type': 'hidden', 'name': k, 'value': v}));
});

//Make it part of the document and submit
$('body').append(form);
form.submit();

//Clean up
form.remove();

JQuery를 많이 사용하지만 기본 JS로도 동일한 작업을 수행 할 수 있습니다.


3

다른 사람들이 언급했듯이 POST 요청을 통해 다운로드 할 양식을 만들어 제출할 수 있습니다. 그러나이 작업을 수동으로 수행 할 필요는 없습니다.

정확히 이것을 수행하기위한 정말 간단한 라이브러리 중 하나는 jquery.redirect 입니다. 표준 jQuery.post방법 과 유사한 API를 제공합니다 .

$.redirect(url, [values, [method, [target]]])

3

이것은 3 살짜리 질문이지만 오늘도 같은 문제가있었습니다. 편집 된 솔루션을 보았지만 이중 요청을해야하기 때문에 성능을 희생 할 수 있다고 생각합니다. 따라서 누구나 서비스를 두 번 호출하지 않는 다른 솔루션이 필요한 경우 이것이 내가 한 방식입니다.

<form id="export-csv-form" method="POST" action="/the/path/to/file">
    <input type="hidden" name="anyValueToPassTheServer" value="">
</form>

이 형식은 서비스를 호출하고 window.location ()을 사용하지 않는 데 사용됩니다. 그런 다음 서비스를 호출하고 파일을 얻으려면 jquery에서 양식 제출 만하면됩니다. 꽤 간단하지만 POST를 사용하여 다운로드 할 수 있습니다 . 전화하는 서비스가 GET 인 경우 이것이 더 쉬울 수 있지만 지금은 그렇지 않습니다.


1
질문이 아약스를 사용하고 있기 때문에 이것은 아약스 게시물이 아닙니다
Nidhin David

위의 문제에 대한 해결책 일뿐이지만 아약스 호출에는 적합하지 않습니다.
노미 알리

1

FileSaver.js 사용했습니다 . CSV 파일을 사용하는 경우에는 이것을 coffescript로 수행했습니다.

  $.ajax
    url: "url-to-server"
    data: "data-to-send"
    success: (csvData)->
      blob = new Blob([csvData], { type: 'text/csv' })
      saveAs(blob, "filename.csv")

가장 복잡한 경우에는 데이터를 올바르게 처리해야한다고 생각합니다. 후드 아래에서 FileSaver.js는 Jonathan Amend 의 대답과 동일한 접근 방식을 구현합니다 .


1
..하지만 일반적으로 iOS에서 파일을 다운로드 할 수 있습니까?
Alex Marshall


1

Jonathan Amends 가 Edge에서 작동하도록 답변 을 얻으려면 다음과 같이 변경했습니다.

var blob = typeof File === 'function'
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

이에

var f = typeof File+"";
var blob = f === 'function' && Modernizr.fileapi
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

오히려 이것을 주석으로 게시하고 싶지만 그 평판이 충분하지 않습니다.


0

다른 소스에서 수집 한 내 솔루션은 다음과 같습니다. 서버 측 구현 :

    String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
    // Set headers
    response.setHeader("content-disposition", "attachment; filename =" + fileName);
    response.setContentType(contentType);
    // Copy file to output stream
    ServletOutputStream servletOutputStream = response.getOutputStream();
    try (InputStream inputStream = new FileInputStream(file)) {
        IOUtils.copy(inputStream, servletOutputStream);
    } finally {
        servletOutputStream.flush();
        Utils.closeQuitely(servletOutputStream);
        fileToDownload = null;
    }

클라이언트 측 구현 (jquery 사용) :

$.ajax({
type: 'POST',
contentType: 'application/json',
    url: <download file url>,
    data: JSON.stringify(postObject),
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert(errorThrown);
    },
    success: function(message, textStatus, response) {
       var header = response.getResponseHeader('Content-Disposition');
       var fileName = header.split("=")[1];
       var blob = new Blob([message]);
       var link = document.createElement('a');
       link.href = window.URL.createObjectURL(blob);
       link.download = fileName;
       link.click();
    }
});   

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

response가 Array Buffer 인 경우 Ajax의 onsuccess 이벤트에서 다음을 시도하십시오.

 if (event.data instanceof ArrayBuffer) {
          var binary = '';
          var bytes = new Uint8Array(event.data);
          for (var i = 0; i < bytes.byteLength; i++) {
              binary += String.fromCharCode(bytes[i])
          }
          $("#some_id").append("<li><img src=\"data:image/png;base64," + window.btoa(binary) + "\"/></span></li>");
          return;
      }
  • 여기서 event.data는 xhr 이벤트의 성공 함수에서 수신 된 응답입니다.

0

아래는 일부 ID로 구성되고 데이터베이스에서 조회되는 일부 목록에 따라 여러 파일을 다운로드하는 솔루션입니다. 파일이 있으면 파일을 확인하고 다운로드 할 수 있습니다. Ajax를 사용하여 각 파일에 대해 C # MVC 작업을 호출하고 있습니다.

그리고 예, 다른 사람들이 말했듯이 jQuery Ajax에서 할 수 있습니다. 나는 Ajax 성공으로 그것을했고 항상 200 응답을 보내고있다.

이것이 핵심입니다.

  success: function (data, textStatus, xhr) {

그리고 이것은 내 코드입니다.

var i = 0;
var max = 0;
function DownloadMultipleFiles() {
            if ($(".dataTables_scrollBody>tr.selected").length > 0) {
                var list = [];
                showPreloader();
                $(".dataTables_scrollBody>tr.selected").each(function (e) {
                    var element = $(this);
                    var orderid = element.data("orderid");
                    var iscustom = element.data("iscustom");
                    var orderlineid = element.data("orderlineid");
                    var folderPath = "";
                    var fileName = "";

                    list.push({ orderId: orderid, isCustomOrderLine: iscustom, orderLineId: orderlineid, folderPath: folderPath, fileName: fileName });
                });
                i = 0;
                max = list.length;
                DownloadFile(list);
            }
        }

그런 다음 전화 :

function DownloadFile(list) {
        $.ajax({
            url: '@Url.Action("OpenFile","OrderLines")',
            type: "post",
            data: list[i],
            xhrFields: {
                responseType: 'blob'
            },
            beforeSend: function (xhr) {
                xhr.setRequestHeader("RequestVerificationToken",
                    $('input:hidden[name="__RequestVerificationToken"]').val());

            },
            success: function (data, textStatus, xhr) {
                // check for a filename
                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 a = document.createElement('a');
                    var url = window.URL.createObjectURL(data);
                    a.href = url;
                    a.download = filename;
                    document.body.append(a);
                    a.click();
                    a.remove();
                    window.URL.revokeObjectURL(url);
                }
                else {
                    getErrorToastMessage("Production file for order line " + list[i].orderLineId + " does not exist");
                }
                i = i + 1;
                if (i < max) {
                    DownloadFile(list);
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {

            },
            complete: function () {
                if(i===max)
                hidePreloader();
            }
        });
    }

C # MVC :

 [HttpPost]
 [ValidateAntiForgeryToken]
public IActionResult OpenFile(OrderLineSimpleModel model)
        {
            byte[] file = null;

            try
            {
                if (model != null)
                {
                    //code for getting file from api - part is missing here as not important for this example
                    file = apiHandler.Get<byte[]>(downloadApiUrl, token);

                    var contentDispositionHeader = new System.Net.Mime.ContentDisposition
                    {
                        Inline = true,
                        FileName = fileName
                    };
                    //    Response.Headers.Add("Content-Disposition", contentDispositionHeader.ToString() + "; attachment");
                    Response.Headers.Add("Content-Type", "application/pdf");
                    Response.Headers.Add("Content-Disposition", "attachment; filename=" + fileName);
                    Response.Headers.Add("Content-Transfer-Encoding", "binary");
                    Response.Headers.Add("Content-Length", file.Length.ToString());

                }
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "Error getting pdf", null);
                return Ok();
            }

            return File(file, System.Net.Mime.MediaTypeNames.Application.Pdf);
        }

응답 200을 반환하는 한 Ajax에서 성공하면 파일이 실제로 존재하는지 여부를 확인할 수 있습니다.이 경우 아래 줄이 거짓이므로 사용자에게 그 사실을 알릴 수 있습니다.

 if (disposition && disposition.indexOf('attachment') !== -1) {

0

@ alain-cruz의 솔루션과 비슷한 솔루션이 필요했지만 여러 번 다운로드하면 nuxt / vue에 있습니다. 브라우저가 여러 파일 다운로드를 차단한다는 것을 알고 있으며 csv 형식의 데이터 집합을 반환하는 API도 있습니다. 처음에는 JSZip을 사용하려고했지만 IE 지원이 필요하므로 여기에 솔루션이 있습니다. 누군가 내가 이것을 개선하도록 도울 수 있다면 그것은 좋을 것이지만 지금까지는 효과가 있습니다.

API는 다음을 반환합니다.

data : {
  body: {
    fileOne: ""col1", "col2", "datarow1.1", "datarow1.2"...so on",
    fileTwo: ""col1", "col2"..."
  }
}

page.vue :

<template>
  <b-link @click.prevent="handleFileExport">Export<b-link>
</template>

export default = {
   data() {
     return {
       fileNames: ['fileOne', 'fileTwo'],
     }
   },
  computed: {
    ...mapState({
       fileOne: (state) => state.exportFile.fileOne,
       fileTwo: (state) => state.exportFile.fileTwo,
    }),
  },
  method: {
    handleExport() {
      //exportFileAction in store/exportFile needs to return promise
      this.$store.dispatch('exportFile/exportFileAction', paramsToSend)
        .then(async (response) => {
           const downloadPrep = this.fileNames.map(async (fileName) => {
           // using lodash to get computed data by the file name
           const currentData = await _.get(this, `${fileName}`);
           const currentFileName = fileName;
           return { currentData, currentFileName };
         });
         const response = await Promise.all(downloadPrep);
         return response;
       })
       .then(async (data) => {
         data.forEach(({ currentData, currentFileName }) => {
           this.forceFileDownload(currentData, currentFileName);
         });
       })
       .catch(console.error);
    },
    forceFileDownload(data, fileName) {
     const url = window.URL
         .createObjectURL(new Blob([data], { type: 'text/csv;charset=utf-8;' }));
     const link = document.createElement('a');
     link.href = url;
     link.setAttribute('download', `${fileName}.csv`);
     document.body.appendChild(link);
     link.click();
   },
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.