브라우저가 파일 다운로드를받는시기 감지


488

사용자가 동적으로 생성 된 파일을 다운로드 할 수있는 페이지가 있습니다. 생성하는 데 시간이 오래 걸리므로 "대기 중"표시기를 표시하고 싶습니다. 문제는 브라우저가 파일을받은 시점을 감지하는 방법을 알 수 없으므로 표시기를 숨길 수 있다는 것입니다.

숨겨진 양식으로 요청을 작성하여 서버에 POST하고 결과를 위해 숨겨진 iframe을 대상으로합니다. 이것은 전체 브라우저 창을 결과로 바꾸지 않습니다. iframe에서 "로드"이벤트를 수신합니다. 다운로드가 완료되면 실행되기를 바랍니다.

파일과 함께 "Content-Disposition : attachment"헤더를 반환하면 브라우저에 "저장"대화 상자가 표시됩니다. 그러나 브라우저는 iframe에서 "로드"이벤트를 발생시키지 않습니다.

내가 시도한 한 가지 접근법은 다중 부분 응답을 사용하는 것입니다. 따라서 빈 HTML 파일과 첨부 된 다운로드 가능한 파일을 보냅니다. 예를 들면 다음과 같습니다.

Content-type: multipart/x-mixed-replace;boundary="abcde"

--abcde
Content-type: text/html

--abcde
Content-type: application/vnd.fdf
Content-Disposition: attachment; filename=foo.fdf

file-content
--abcde

이것은 Firefox에서 작동합니다. 빈 HTML 파일을 수신하고 "로드"이벤트를 시작한 다음 다운로드 가능한 파일에 대한 "저장"대화 상자를 표시합니다. 그러나 IE와 Safari에서는 실패합니다. IE는 "로드"이벤트를 발생 시키지만 파일을 다운로드하지 않으며 Safari는 파일을 잘못된 이름과 내용 유형으로 다운로드하고 "로드"이벤트를 발생시키지 않습니다.

다른 방법은 파일 작성을 시작하기 위해 전화를 걸고 서버가 준비 될 때까지 폴링 한 다음 이미 작성된 파일을 다운로드하는 것입니다. 그러나 서버에서 임시 파일을 만드는 것을 피하고 싶습니다.

더 좋은 아이디어가 있습니까?


4
IE의 어떤 버전도 multipart / x-mixed-replace를 지원하지 않습니다.
EricLaw

고마워 에릭-알아두면 좋겠다. 나는 그 접근법으로 더 이상 시간을 낭비하지 않을 것입니다.
JW.

신뢰할 수있는 방법은 서버 푸시 알림 (ASP.NET 사용자를위한 SignalR) 인 것 같습니다.
dudeNumber4

1
bennadel.com/blog/…- 이것은 간단한 해결책입니다
Mateen

1
@ 친구 감사합니다! 정말 간단합니다
Fai Zal Dong

답변:


451

하나의 가능한 솔루션 은 클라이언트에서 JavaScript를 사용합니다.

클라이언트 알고리즘 :

  1. 임의의 고유 토큰을 생성하십시오.
  2. 다운로드 요청을 제출하고 GET / POST 필드에 토큰을 포함 시키십시오.
  3. "대기 중"표시기를 표시하십시오.
  4. 타이머를 시작하고 약 1 초마다 "fileDownloadToken"(또는 결정한 것)이라는 쿠키를 찾으십시오.
  5. 쿠키가 존재하고 해당 값이 토큰과 일치하면 "대기 중"표시기를 숨 깁니다.

서버 알고리즘 :

  1. 요청에서 GET / POST 필드를 찾으십시오.
  2. 값이 비어 있지 않으면 쿠키 (예 : "fileDownloadToken")를 삭제하고 해당 값을 토큰 값으로 설정하십시오.

클라이언트 소스 코드 (자바 스크립트) :

function getCookie( name ) {
  var parts = document.cookie.split(name + "=");
  if (parts.length == 2) return parts.pop().split(";").shift();
}

function expireCookie( cName ) {
    document.cookie = 
        encodeURIComponent(cName) + "=deleted; expires=" + new Date( 0 ).toUTCString();
}

function setCursor( docStyle, buttonStyle ) {
    document.getElementById( "doc" ).style.cursor = docStyle;
    document.getElementById( "button-id" ).style.cursor = buttonStyle;
}

function setFormToken() {
    var downloadToken = new Date().getTime();
    document.getElementById( "downloadToken" ).value = downloadToken;
    return downloadToken;
}

var downloadTimer;
var attempts = 30;

// Prevents double-submits by waiting for a cookie from the server.
function blockResubmit() {
    var downloadToken = setFormToken();
    setCursor( "wait", "wait" );

    downloadTimer = window.setInterval( function() {
        var token = getCookie( "downloadToken" );

        if( (token == downloadToken) || (attempts == 0) ) {
            unblockSubmit();
        }

        attempts--;
    }, 1000 );
}

function unblockSubmit() {
  setCursor( "auto", "pointer" );
  window.clearInterval( downloadTimer );
  expireCookie( "downloadToken" );
  attempts = 30;
}

서버 코드 예 (PHP) :

$TOKEN = "downloadToken";

// Sets a cookie so that when the download begins the browser can
// unblock the submit button (thus helping to prevent multiple clicks).
// The false parameter allows the cookie to be exposed to JavaScript.
$this->setCookieToken( $TOKEN, $_GET[ $TOKEN ], false );

$result = $this->sendFile();

어디:

public function setCookieToken(
    $cookieName, $cookieValue, $httpOnly = true, $secure = false ) {

    // See: http://stackoverflow.com/a/1459794/59087
    // See: http://shiflett.org/blog/2006/mar/server-name-versus-http-host
    // See: http://stackoverflow.com/a/3290474/59087
    setcookie(
        $cookieName,
        $cookieValue,
        2147483647,            // expires January 1, 2038
        "/",                   // your path
        $_SERVER["HTTP_HOST"], // your domain
        $secure,               // Use true over HTTPS
        $httpOnly              // Set true for $AUTH_COOKIE_NAME
    );
}

4
멋진 아이디어로, jQuery / C #을 사용하여 여러 파일을 다운로드하는 것에 대한이 답변 의 기본 프레임 워크로 사용했습니다
Greg

7
document.cookies에 downloadToken이 포함되어 있지 않으면 쿠키 경로를 확인하십시오. 필자의 경우 기본적으로 경로가 비어 있지만 서버 측에서 경로를 "/"(예 : Java의 cookie.setPath ( "/"))로 설정해야했습니다. 얼마 동안 나는 문제가 특별한 'localhost'도메인 쿠키 처리 ( stackoverflow.com/questions/1134290/… ) 라고 생각 했지만 결국에는 문제가되지 않았다. 읽을 가치가 있지만 다른 사람들을위한 것일 수도 있습니다.
jlpp

2
귀하의 솔루션에 대해 더 깊이 들어가기 전에 @bulltorious가 교차 도메인 파일 다운로드 요청과 함께 작동하는지 궁금합니다. 쿠키 제한이 쿠키를 손상시킬 것이라고 생각합니까?
kiks73

5
Brilliant-파일 다운로드의 일부로 쿠키를 포함시킬 수 있었던 것은 100 년 동안 나에게 일어나지 않았을 것입니다. 감사합니다!!
freefaller

8
다른 사람들이 지적 했듯이이 솔루션은 서버가 파일 시간을 준비하기를 기다리는 문제의 일부만 해결합니다. 파일의 크기와 연결 속도에 따라 문제의 다른 부분은 실제로 전체 파일을 클라이언트에 가져 오는 데 걸리는 시간입니다. 그리고이 솔루션으로는 해결되지 않습니다.
AsGoodAsIt11

27

매우 간단한 (그리고 절름발이) 한 줄 솔루션은 window.onblur()이벤트를 사용 하여 로딩 대화 상자를 닫는 것입니다. 물론 시간이 너무 오래 걸리고 사용자가 이메일 읽기와 같은 다른 작업을 수행하기로 결정하면 로딩 대화 상자가 닫힙니다.


이것은 onbeforeunload감사를 사용하여 트리거 된 파일 다운로드에 대한 로딩 오버레이를 제거하는 데 이상적인 간단한 접근 방식입니다 .
wf4

5
모든 브라우저에서 작동하는 것은 아닙니다 (일부는 Safari, 일부 IE 버전 등 다운로드 워크 플로의 일부로 현재 창을 떠나거나 흐리게 표시하지 않습니다).
hiattp

4
Chrome 및 기타 브라우저는이 상태가 실패하는 파일을 자동 다운로드합니다.
Lucky

@Lucky는 기본적으로 만 사용됩니다. Chrome 사용자는 다운로드를 저장할 위치를 지정하여 대화 상자를 볼 수 있습니다.
ESR

2
tabchange에서 블러 또는 창 밖에서의 동작을 활성화하기 때문에 나쁜 생각
Michael

14

오래된 실, 알아

그러나 Google이 이끄는 것은 내 솔루션에 관심이있을 수 있습니다. 매우 간단하지만 신뢰할 수 있습니다. 실제 진행 메시지를 표시 할 수 있으며 기존 프로세스에 쉽게 연결할 수 있습니다.

처리하는 스크립트 (내 문제는 http를 통해 파일을 검색하여 zip으로 전달)가 세션에 상태를 기록합니다.

상태는 매초마다 폴링되고 표시됩니다. 그 전부입니다 (좋아요, 아닙니다. 많은 세부 사항 (예 : 동시 다운로드)을 처리해야하지만 시작하기에 좋은 곳입니다 ;-)).

다운로드 페이지 :

    <a href="download.php?id=1" class="download">DOWNLOAD 1</a>
    <a href="download.php?id=2" class="download">DOWNLOAD 2</a>
    ...
    <div id="wait">
    Please wait...
    <div id="statusmessage"></div>
    </div>
    <script>
//this is jquery
    $('a.download').each(function()
       {
        $(this).click(
             function(){
               $('#statusmessage').html('prepare loading...');
               $('#wait').show();
               setTimeout('getstatus()', 1000);
             }
          );
        });
    });
    function getstatus(){
      $.ajax({
          url: "/getstatus.php",
          type: "POST",
          dataType: 'json',
          success: function(data) {
            $('#statusmessage').html(data.message);
            if(data.status=="pending")
              setTimeout('getstatus()', 1000);
            else
              $('#wait').hide();
          }
      });
    }
    </script>

getstatus.php

<?php
session_start();
echo json_encode($_SESSION['downloadstatus']);
?>

download.php

    <?php
    session_start();
    $processing=true;
    while($processing){
      $_SESSION['downloadstatus']=array("status"=>"pending","message"=>"Processing".$someinfo);
      session_write_close();
      $processing=do_what_has_2Bdone();
      session_start();
    }
      $_SESSION['downloadstatus']=array("status"=>"finished","message"=>"Done");
//and spit the generated file to the browser
    ?>

3
그러나 사용자에게 여러 개의 창이 있거나 다운로드가 열려 있다면? 또한 서버에 대한 중복 호출
Yuki

3
한 사용자의 연결이 여러 개인 경우 session_start ()는 사용자의 세션을 잠그고 다른 모든 프로세스가 액세스하지 못하므로 다른 연결이 종료되기를 기다립니다.
Honza Kuchař

2
.each()이벤트 등록 에 사용할 필요는 없습니다 . 그냥 말해$('a.download').click()
robisrob

안에 코드를 평가하지 마십시오 setTimeout('getstatus()', 1000);. fn을 직접 사용하십시오.setTimeout(getstatus, 1000);
Roko C. Buljan

11

다음을 사용하여 Blob을 다운로드하고 다운로드 후 object-url을 취소하십시오. 크롬과 파이어 폭스에서 작동합니다!

function download(blob){
    var url = URL.createObjectURL(blob);
    console.log('create ' + url);

    window.addEventListener('focus', window_focus, false);
    function window_focus(){
        window.removeEventListener('focus', window_focus, false);                   
        URL.revokeObjectURL(url);
        console.log('revoke ' + url);
    }
    location.href = url;
}

파일 다운로드 대화 상자가 닫히면 창에서 포커스가 다시 돌아가서 포커스 이벤트가 트리거됩니다.


여전히 창 전환 및 리턴 문제가있어 모달을 숨길 수 있습니다.
dudeNumber4

9
하단 트레이에 다운로드하는 Chrome과 같은 브라우저는 창을 흐리게 / 초점을 맞추지 않습니다.
Coleman

10

Elmer의 예를 기반으로 내 솔루션을 준비했습니다. 정의 된 다운로드 클래스로 요소를 클릭 하면 화면에 사용자 정의 메시지를 표시 할 수 있습니다. 나는 초점을 사용했다 트리거를 하여 메시지를 숨겼습니다.

자바 스크립트

$(function(){$('.download').click(function() { ShowDownloadMessage(); }); })

function ShowDownloadMessage()
{
     $('#message-text').text('your report is creating, please wait...');
     $('#message').show();
     window.addEventListener('focus', HideDownloadMessage, false);
}

function HideDownloadMessage(){
    window.removeEventListener('focus', HideDownloadMessage, false);                   
    $('#message').hide();
}

HTML

<div id="message" style="display: none">
    <div id="message-screen-mask" class="ui-widget-overlay ui-front"></div>
    <div id="message-text" class="ui-dialog ui-widget ui-widget-content ui-corner-all ui-front ui-draggable ui-resizable waitmessage">please wait...</div>
</div>

이제 다운로드 할 요소를 구현해야합니다.

<a class="download" href="file://www.ocelot.com.pl/prepare-report">Download report</a>

또는

<input class="download" type="submit" value="Download" name="actionType">

다운로드를 클릭 할 때마다 보고서가 작성되고 있다는 메시지가 표시됩니다 . 잠시 기다려주십시오 ...


2
사용자가 창을 클릭하면 어떻게됩니까?
Tom Roggero

이것은 내가 찾던 것입니다.
세르지오

숨김은 () 내 경우에는 전화 받고 있지 않습니다
Prashant Pimpale

8

나는 까다로운 답변에 설명 된 것과 유사한 기술을 구현하는 간단한 JavaScript 클래스를 작성했습니다 . 나는 그것이 누군가에게 유용 할 수 있기를 바랍니다. GitHub 프로젝트를 response-monitor.js 라고합니다.

기본적으로 spin.js 를 대기 표시기로 사용하지만 사용자 지정 표시기 구현을위한 콜백 세트도 제공합니다.

JQuery는 지원되지만 필수는 아닙니다.

주목할만한 특징

  • 간단한 통합
  • 의존성 없음
  • JQuery 플러그인 (선택 사항)
  • Spin.js 통합 (선택 사항)
  • 이벤트 모니터링을위한 구성 가능한 콜백
  • 여러 개의 동시 요청 처리
  • 서버 측 오류 감지
  • 타임 아웃 감지
  • 크로스 브라우저

사용법 예

HTML

<!-- the response monitor implementation -->
<script src="response-monitor.js"></script>

<!-- optional JQuery plug-in -->
<script src="response-monitor.jquery.js"></script> 

<a class="my_anchors" href="/report?criteria1=a&criteria2=b#30">Link 1 (Timeout: 30s)</a>
<a class="my_anchors" href="/report?criteria1=b&criteria2=d#10">Link 2 (Timeout: 10s)</a>

<form id="my_form" method="POST">
    <input type="text" name="criteria1">
    <input type="text" name="criteria2">
    <input type="submit" value="Download Report">
</form>

클라이언트 (일반 JavaScript)

//registering multiple anchors at once
var my_anchors = document.getElementsByClassName('my_anchors');
ResponseMonitor.register(my_anchors); //clicking on the links initiates monitoring

//registering a single form
var my_form = document.getElementById('my_form');
ResponseMonitor.register(my_form); //the submit event will be intercepted and monitored

클라이언트 (JQuery)

$('.my_anchors').ResponseMonitor();
$('#my_form').ResponseMonitor({timeout: 20});

콜백이있는 클라이언트 (JQuery)

//when options are defined, the default spin.js integration is bypassed
var options = {
    onRequest: function(token){
        $('#cookie').html(token);
        $('#outcome').html('');
        $('#duration').html(''); 
    },
    onMonitor: function(countdown){
        $('#duration').html(countdown); 
    },
    onResponse: function(status){
        $('#outcome').html(status==1?'success':'failure');
    },
    onTimeout: function(){
        $('#outcome').html('timeout');
    }
};

//monitor all anchors in the document
$('a').ResponseMonitor(options);

서버 (PHP)

$cookiePrefix = 'response-monitor'; //must match the one set on the client options
$tokenValue = $_GET[$cookiePrefix];
$cookieName = $cookiePrefix.'_'.$tokenValue; //ex: response-monitor_1419642741528

//this value is passed to the client through the ResponseMonitor.onResponse callback
$cookieValue = 1; //for ex, "1" can interpret as success and "0" as failure

setcookie(
    $cookieName,
    $cookieValue,
    time()+300,            // expire in 5 minutes
    "/",
    $_SERVER["HTTP_HOST"],
    true,
    false
);

header('Content-Type: text/plain');
header("Content-Disposition: attachment; filename=\"Response.txt\"");

sleep(5); //simulate whatever delays the response
print_r($_REQUEST); //dump the request in the text file

더 많은 예제를 보려면 리포지토리 의 예제 폴더를 확인하십시오 .


5

나는 파티에 매우 늦었지만 다른 사람이 내 솔루션을 알고 싶다면 여기에 올려 놓을 것입니다.

나는이 정확한 문제로 실제로 어려움을 겪었지만 iframe을 사용하여 실행 가능한 솔루션을 찾았습니다 (나도 알고 있습니다.

파일을 생성 한 다음 다운로드 한 별도의 PHP 스크립트를 시작하는 html 페이지가 있습니다. html 페이지에서 html 헤더에 다음 jquery를 사용했습니다 (jquery 라이브러리도 포함해야 함).

<script>
    $(function(){
        var iframe = $("<iframe>", {name: 'iframe', id: 'iframe',}).appendTo("body").hide();
        $('#click').on('click', function(){
            $('#iframe').attr('src', 'your_download_script.php');
        });
        $('iframe').load(function(){
            $('#iframe').attr('src', 'your_download_script.php?download=yes'); <!--on first iframe load, run script again but download file instead-->
            $('#iframe').unbind(); <!--unbinds the iframe. Helps prevent against infinite recursion if the script returns valid html (such as echoing out exceptions) -->
        });
    });
</script>

your_download_script.php에서 다음을 갖추십시오.

function downloadFile($file_path) {
    if (file_exists($file_path)) {
        header('Content-Description: File Transfer');
        header('Content-Type: text/csv');
        header('Content-Disposition: attachment; filename=' . basename($file_path));
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file_path));
        ob_clean();
        flush();
        readfile($file_path);
        exit();
    }
}


$_SESSION['your_file'] = path_to_file; //this is just how I chose to store the filepath

if (isset($_REQUEST['download']) && $_REQUEST['download'] == 'yes') {
    downloadFile($_SESSION['your_file']);
} else {
    *execute logic to create the file*
}

이 문제를 해결하기 위해 jquery는 먼저 iframe에서 PHP 스크립트를 시작합니다. 파일이 생성되면 iframe이로드됩니다. 그런 다음 jquery는 스크립트에 파일을 다운로드하도록 요청하는 요청 변수와 함께 스크립트를 다시 시작합니다.

다운로드 및 파일 생성을 한 번에 수행 할 수없는 이유는 php header () 함수 때문입니다. header ()를 사용하는 경우 스크립트를 웹 페이지가 아닌 다른 것으로 변경하면 jquery는 다운로드 스크립트가 '로드'된 것으로 인식하지 않습니다. 브라우저가 파일을 수신 할 때 이것이 반드시 감지되지는 않을 수도 있지만 문제는 내 것과 비슷하다고 들었습니다.


5

동적으로 생성하는 파일을 스트리밍하고 실시간 서버-클라이언트 메시징 라이브러리를 구현 한 경우 클라이언트에게 매우 쉽게 경고 할 수 있습니다.

내가 좋아하고 추천하는 서버-클라이언트 메시징 라이브러리는 Socket.io (via via.js)입니다. 서버 스크립트가 다운로드를 위해 스트리밍되는 파일을 생성 한 후 해당 스크립트의 마지막 줄은 Socket.io에 메시지를 보내 클라이언트에게 알림을 보냅니다. 클라이언트에서 Socket.io는 서버에서 생성 된 수신 메시지를 수신하고 사용자가 메시지를 처리 ​​할 수 ​​있도록합니다. 다른 방법보다이 방법을 사용하면 스트리밍이 완료된 후 "참"완료 이벤트를 감지 할 수 있다는 이점이 있습니다.

예를 들어, 다운로드 링크를 클릭 한 후 통화 중 표시기를 표시하고, 파일을 스트리밍하고, 스트리밍 스크립트의 마지막 줄에있는 서버에서 Socket.io로 메시지를 보내고, 클라이언트에서 알림을 듣고, 알림을받을 수 있습니다 통화 중 표시기를 숨겨 UI를 업데이트하십시오.

이 질문에 대한 답변을 읽는 대부분의 사람들은 이러한 유형의 설정이 없을 수도 있지만이 정확한 솔루션을 사용하여 내 프로젝트에 큰 영향을 미쳤으며 훌륭하게 작동합니다.

Socket.io는 설치 및 사용이 매우 쉽습니다. 더보기 : http://socket.io/


5

"브라우저가 파일 다운로드를받는시기를 감지하는 방법?"
나는 그 구성에서 같은 문제에 직면했다 :
struts 1.2.9
jquery-1.3.2.
jquery-ui-1.7.1.custom
IE 11
자바 5


쿠키가있는 내 솔루션 :
-클라이언트 측 :
양식을 제출 할 때 자바 스크립트 함수를 호출하여 페이지를 숨기고 대기 스피너를로드하십시오.

function loadWaitingSpinner(){
... hide your page and show your spinner ...
}

그런 다음 쿠키가 서버에서 오는지 500ms마다 확인하는 함수를 호출하십시오.

function checkCookie(){
    var verif = setInterval(isWaitingCookie,500,verif);
}

쿠키가 발견되면 500ms마다 검사를 중지하고 쿠키를 만료 한 다음 함수를 호출하여 페이지로 돌아와 대기중인 스피너 ( removeWaitingSpinner () )를 제거하십시오 . 다른 파일을 다시 다운로드하려면 쿠키를 만료시키는 것이 중요합니다!

function isWaitingCookie(verif){
    var loadState = getCookie("waitingCookie");
    if (loadState == "done"){
        clearInterval(verif);
        document.cookie = "attenteCookie=done; expires=Tue, 31 Dec 1985 21:00:00 UTC;";
        removeWaitingSpinner();
    }
}
    function getCookie(cookieName){
        var name = cookieName + "=";
        var cookies = document.cookie
        var cs = cookies.split(';');
        for (var i = 0; i < cs.length; i++){
            var c = cs[i];
            while(c.charAt(0) == ' ') {
                c = c.substring(1);
            }
            if (c.indexOf(name) == 0){
                return c.substring(name.length, c.length);
            }
        }
        return "";
    }
function removeWaitingSpinner(){
... come back to your page and remove your spinner ...
}

-서버 측 :
서버 프로세스가 끝나면 응답에 쿠키를 추가하십시오. 파일을 다운로드 할 준비가되면 쿠키가 클라이언트로 전송됩니다.

Cookie waitCookie = new Cookie("waitingCookie", "done");
response.addCookie(waitCookie);

누군가를 도울 수 있도록 노력하겠습니다!


완벽하게 작동합니다. 이 아름다운 샘플에 감사드립니다.
Sedat Kumcu

4

사용자가 파일 생성을 트리거하면 해당 "다운로드"에 고유 ID를 지정하고 몇 초마다 새로 고침 (또는 AJAX로 확인)하는 페이지로 사용자를 보낼 수 있습니다. 파일이 완료되면 동일한 고유 ID로 저장하고 ...

  • 파일이 준비되면 다운로드하십시오.
  • 파일이 준비되지 않은 경우 진행률을 표시하십시오.

그런 다음 전체 iframe / 대기 / 브라우저 창 엉망을 건너 뛸 수는 있지만 정말 우아한 해결책이 있습니다.


위에서 언급 한 임시 파일 접근 방식처럼 들립니다. 내 생각이 불가능한 것으로 판명되면 이런 식으로 할 수는 있지만 피하기를 바랐습니다.
JW.

3

서버에서 파일을 생성하고 저장하지 않으려면 진행중인 파일, 파일 완성과 같은 상태를 저장 하시겠습니까? "대기 중"페이지는 파일 생성이 완료된시기를 알기 위해 서버를 폴링 할 수 있습니다. 브라우저가 다운로드를 시작했는지 확신 할 수 없지만 확신 할 수 있습니다.


2

방금이 같은 문제가 발생했습니다. 내 솔루션은 이미 많은 임시 파일을 생성했기 때문에 임시 파일을 사용하는 것이 었습니다. 이 양식은 다음과 함께 제출됩니다.

var microBox = {
    show : function(content) {
        $(document.body).append('<div id="microBox_overlay"></div><div id="microBox_window"><div id="microBox_frame"><div id="microBox">' +
        content + '</div></div></div>');
        return $('#microBox_overlay');
    },

    close : function() {
        $('#microBox_overlay').remove();
        $('#microBox_window').remove();
    }
};

$.fn.bgForm = function(content, callback) {
    // Create an iframe as target of form submit
    var id = 'bgForm' + (new Date().getTime());
    var $iframe = $('<iframe id="' + id + '" name="' + id + '" style="display: none;" src="about:blank"></iframe>')
        .appendTo(document.body);
    var $form = this;
    // Submittal to an iframe target prevents page refresh
    $form.attr('target', id);
    // The first load event is called when about:blank is loaded
    $iframe.one('load', function() {
        // Attach listener to load events that occur after successful form submittal
        $iframe.load(function() {
            microBox.close();
            if (typeof(callback) == 'function') {
                var iframe = $iframe[0];
                var doc = iframe.contentWindow.document;
                var data = doc.body.innerHTML;
                callback(data);
            }
        });
    });

    this.submit(function() {
        microBox.show(content);
    });

    return this;
};

$('#myForm').bgForm('Please wait...');

파일을 생성하는 스크립트 끝에서 나는 가지고 있습니다 :

header('Refresh: 0;url=fetch.php?token=' . $token);
echo '<html></html>';

이로 인해 iframe의로드 이벤트가 시작됩니다. 그런 다음 대기 메시지가 닫히고 파일 다운로드가 시작됩니다. IE7 및 Firefox에서 테스트되었습니다.


2

내 경험상 이것을 처리하는 두 가지 방법이 있습니다.

  1. 다운로드시 단기 쿠키를 설정하고 JavaScript가 계속 존재하는지 확인하십시오. 진짜 문제는 쿠키 수명을 올바르게 유지하는 것입니다. 너무 짧아 JS가 쿠키를 너무 오래 놓칠 수 있으며 다른 다운로드의 다운로드 화면을 취소 할 수 있습니다. 검색시 JS를 사용하여 쿠키를 제거하면 일반적으로이 문제가 해결됩니다.
  2. fetch / XHR을 사용하여 파일을 다운로드하십시오. 파일 다운로드가 완료되는 시점을 정확히 알고있을뿐만 아니라 XHR을 사용하는 경우 진행률 이벤트를 사용하여 진행률 표시 줄을 표시 할 수 있습니다! IE / Edge의 msSaveBlob 과 Firefox / Chrome 의 다운로드 링크 ( 이와 같은 )를 사용 하여 결과 blob을 저장하십시오 . 이 방법의 문제점은 iOS Safari가 Blob 다운로드를 올바르게 처리하지 못하는 것입니다. Blob을 FileReader를 사용하여 데이터 URL로 변환하고 새 창에서 열 수는 있지만 파일을 여는 것이 아니라 저장하지는 않습니다.

2

인사말, 나는 주제가 오래되었다는 것을 알고 있지만 다른 곳에서 본 해결책을 남겼습니다.

/**
 *  download file, show modal
 *
 * @param uri link
 * @param name file name
 */
function downloadURI(uri, name) {
// <------------------------------------------       Do someting (show loading)
    fetch(uri)
        .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 = name;
            document.body.appendChild(a);
            a.click();
            window.URL.revokeObjectURL(url);
            // <----------------------------------------  Detect here (hide loading)
            alert('File detected'));
        })
        .catch(() => alert('An error sorry'));
}

당신은 그것을 사용할 수 있습니다 :

downloadURI("www.linkToFile.com", "file.name");

1

문서에있는 것이 아니라 저장된 파일을 다운로드 한 경우, 현재 문서의 범위에 있지 않고 브라우저에서 별도의 프로세스이기 때문에 다운로드가 완료되는시기를 결정할 방법이 없습니다.


8
나는 다운로드가 언제 완료 되는지에 대해 너무 걱정하지 않는다 . 다운로드가 언제 시작되는지 식별 할 수 있다면 충분할 것이다.
JW.

0

문제는 파일이 생성되는 동안 '대기'표시기가 있고 파일이 다운로드되면 정상으로 돌아 오는 것입니다. 내가 좋아하는 방법은 숨겨진 iFrame을 사용하고 프레임의 onload 이벤트를 연결하여 다운로드가 시작될 때 내 페이지에 알리는 것입니다. 그러나 첨부 파일 헤더 토큰과 같이 파일 다운로드를 위해 IE에서 onload가 실행되지 않습니다. 서버 폴링은 작동하지만 추가 복잡성을 싫어합니다. 그래서 여기 내가하는 일이 있습니다.

  • 평소와 같이 숨겨진 iFrame을 대상으로합니다.
  • 컨텐츠를 생성하십시오. 2 분 안에 절대 시간 초과로 캐시하십시오.
  • 본질적으로 생성기 페이지를 다시 호출하는 호출 클라이언트로 자바 스크립트 리디렉션을 보냅니다. 참고 : 이것은 onload 이벤트가 일반 페이지처럼 작동하기 때문에 IE에서 발생합니다.
  • 캐시에서 컨텐츠를 제거하여 클라이언트로 보냅니다.

고지 사항, 캐싱이 추가 될 수 있기 때문에 바쁜 사이트에서는이 작업을 수행하지 마십시오. 그러나 실제로 장기 실행 프로세스를 사용하는 사이트가 어쨌든 스레드에 굶주리게됩니다.

다음은 코드 숨김의 모양이며 실제로 필요한 것입니다.

public partial class Download : System.Web.UI.Page
{
    protected System.Web.UI.HtmlControls.HtmlControl Body;

    protected void Page_Load( object sender, EventArgs e )
    {
        byte[ ] data;
        string reportKey = Session.SessionID + "_Report";

        // Check is this page request to generate the content
        //    or return the content (data query string defined)
        if ( Request.QueryString[ "data" ] != null )
        {
            // Get the data and remove the cache
            data = Cache[ reportKey ] as byte[ ];
            Cache.Remove( reportKey );

            if ( data == null )                    
                // send the user some information
                Response.Write( "Javascript to tell user there was a problem." );                    
            else
            {
                Response.CacheControl = "no-cache";
                Response.AppendHeader( "Pragma", "no-cache" );
                Response.Buffer = true;

                Response.AppendHeader( "content-disposition", "attachment; filename=Report.pdf" );
                Response.AppendHeader( "content-size", data.Length.ToString( ) );
                Response.BinaryWrite( data );
            }
            Response.End();                
        }
        else
        {
            // Generate the data here. I am loading a file just for an example
            using ( System.IO.FileStream stream = new System.IO.FileStream( @"C:\1.pdf", System.IO.FileMode.Open ) )
                using ( System.IO.BinaryReader reader = new System.IO.BinaryReader( stream ) )
                {
                    data = new byte[ reader.BaseStream.Length ];
                    reader.Read( data, 0, data.Length );
                }

            // Store the content for retrieval              
            Cache.Insert( reportKey, data, null, DateTime.Now.AddMinutes( 5 ), TimeSpan.Zero );

            // This is the key bit that tells the frame to reload this page 
            //   and start downloading the content. NOTE: Url has a query string 
            //   value, so that the content isn't generated again.
            Body.Attributes.Add("onload", "window.location = 'binary.aspx?data=t'");
        }
    }

0

다운로드 대화 상자가 표시 될 때까지 메시지 또는 로더 gif 만 표시하려는 경우 메시지를 숨겨진 컨테이너에 넣고 다운로드 할 파일을 생성하는 버튼을 클릭하면 컨테이너가 표시됩니다. 그런 다음 jquery 또는 javascript를 사용하여 버튼의 포커스 아웃 이벤트를 포착하여 메시지가 포함 된 컨테이너를 숨 깁니다.


0

Blob을 사용한 Xmlhttprequest가 옵션이 아닌 경우 새 창에서 파일을 열고 eny 요소가 간격으로 해당 창 본문에 채워져 있는지 확인할 수 있습니다.

var form = document.getElementById("frmDownlaod");
 form.setAttribute("action","downoad/url");
 form.setAttribute("target","downlaod");
 var exportwindow = window.open("", "downlaod", "width=800,height=600,resizable=yes");
 form.submit();

var responseInterval = setInterval(function(){
	var winBody = exportwindow.document.body
	if(winBody.hasChildNodes()) // or 'downoad/url' === exportwindow.document.location.href
	{
		clearInterval(responseInterval);
		// do your work
		// if there is error page configured your application for failed requests, check for those dom elemets 
	}
}, 1000)
//Better if you specify maximun no of intervals


0

이 Java / Spring 예제는 "Ending ..."표시기를 숨기는 다운로드의 끝을 감지합니다.

접근 방식 : JS 측에서 최대 만료 기간이 2 분인 쿠키를 설정하고 쿠키 만료를 위해 매초마다 폴링합니다 . 그런 다음 서버 측이이 쿠키를 이전 쿠키로 대체합니다. 서버 프로세스의 완료 - 만료 나이. JS 폴링에서 쿠키 만료가 감지되면 "로드 중 ..."이 숨겨집니다.

JS 사이드

function buttonClick() { // Suppose this is the handler for the button that starts
    $("#loadingProgressOverlay").show();  // show loading animation
    startDownloadChecker("loadingProgressOverlay", 120);
    // Here you launch the download URL...
    window.location.href = "myapp.com/myapp/download";
}

// This JS function detects the end of a download.
// It does timed polling for a non-expired Cookie, initially set on the 
// client-side with a default max age of 2 min., 
// but then overridden on the server-side with an *earlier* expiration age 
// (the completion of the server operation) and sent in the response. 
// Either the JS timer detects the expired cookie earlier than 2 min. 
// (coming from the server), or the initial JS-created cookie expires after 2 min. 
function startDownloadChecker(imageId, timeout) {

    var cookieName = "ServerProcessCompleteChecker";  // Name of the cookie which is set and later overridden on the server
    var downloadTimer = 0;  // reference to timer object    

    // The cookie is initially set on the client-side with a specified default timeout age (2 min. in our application)
    // It will be overridden on the server side with a new (earlier) expiration age (the completion of the server operation), 
    // or auto-expire after 2 min.
    setCookie(cookieName, 0, timeout);

    // set timer to check for cookie every second
    downloadTimer = window.setInterval(function () {

        var cookie = getCookie(cookieName);

        // If cookie expired (NOTE: this is equivalent to cookie "doesn't exist"), then clear "Loading..." and stop polling
        if ((typeof cookie === 'undefined')) {
            $("#" + imageId).hide();
            window.clearInterval(downloadTimer);
        }

    }, 1000); // Every second
}

// These are helper JS functions for setting and retrieving a Cookie
function setCookie(name, value, expiresInSeconds) {
    var exdate = new Date();
    exdate.setTime(exdate.getTime() + expiresInSeconds * 1000);
    var c_value = escape(value) + ((expiresInSeconds == null) ? "" : "; expires=" + exdate.toUTCString());
    document.cookie = name + "=" + c_value + '; path=/';
}

function getCookie(name) {
    var parts = document.cookie.split(name + "=");
    if (parts.length == 2 ) {
        return parts.pop().split(";").shift();
    }
}

자바 / 스프링 서버 측

    @RequestMapping("/download")
    public String download(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //... Some logic for downloading, returning a result ...

        // Create a Cookie that will override the JS-created Max-Age-2min Cookie 
        // with an earlier expiration (same name)
        Cookie myCookie = new Cookie("ServerProcessCompleteChecker", "-1");
        myCookie.setMaxAge(0); // this is immediate expiration, 
                               // but can also add +3 sec. for any flushing concerns
        myCookie.setPath("/");
        response.addCookie(myCookie);
        //... -- presumably the download is writing to the Output Stream...
        return null;
}

쿠키는 JS 스크립트에 의해 생성되지만 컨트롤러에 의해 업데이트되지 않고 원래 값 (0)을 유지합니다. 페이지를 새로 고치지 않고 쿠키 값을 어떻게 업데이트 할 수 있습니까?
Shessuky

그게 이상하다 - 당신은 이름은 보장 할 수 정확히 맞습니까? 이름이 일치하면 쿠키를 덮어 씁니다. 알려주세요
유전자 b.

원래 값이 0이 아닙니다. JS에 설정된 원래 값은 2 분입니다. 서버가 수정해야하는 새로운 값은 0입니다.
gene b.

또한이 작업을 수행하고 있습니까? myCookie.setPath("/"); response.addCookie(myCookie);
유전자 b.

response.getOutputStream ();을 하기 전에 쿠키를 추가해야한다는 것을 알아 냈습니다 . (다운로드 파일을 추가하기 위해 응답 출력 스트림 가져 오기), 그 단계 후에 수행했을 때 고려되지 않았습니다
Shessuky

0

Primefaces는 쿠키 폴링도 사용합니다

https://github.com/primefaces/primefaces/blob/32bb00299d00e50b2cba430638468a4145f4edb0/src/main/resources/META-INF/resources/primefaces/core/core.js#L458

    monitorDownload: function(start, complete, monitorKey) {
        if(this.cookiesEnabled()) {
            if(start) {
                start();
            }

            var cookieName = monitorKey ? 'primefaces.download_' + monitorKey : 'primefaces.download';
            window.downloadMonitor = setInterval(function() {
                var downloadComplete = PrimeFaces.getCookie(cookieName);

                if(downloadComplete === 'true') {
                    if(complete) {
                        complete();
                    }
                    clearInterval(window.downloadMonitor);
                    PrimeFaces.setCookie(cookieName, null);
                }
            }, 1000);
        }
    },

-2

버튼 / 링크를 클릭하면 iframe을 생성하고 본문에 추가하십시오.

                  $('<iframe />')
                 .attr('src', url)
                 .attr('id','iframe_download_report')
                 .hide()
                 .appendTo('body'); 

지연된 iframe을 생성하고 다운로드 후 삭제하십시오.

                            var triggerDelay =   100;
                            var cleaningDelay =  20000;
                            var that = this;
                            setTimeout(function() {
                                var frame = $('<iframe style="width:1px; height:1px;" class="multi-download-frame"></iframe>');
                                frame.attr('src', url+"?"+ "Content-Disposition: attachment ; filename="+that.model.get('fileName'));
                                $(ev.target).after(frame);
                                setTimeout(function() {
                                    frame.remove();
                                }, cleaningDelay);
                            }, triggerDelay);

정보가 부족하여 "로드를 숨길 때"문제를 해결하지 못합니다.
Tom Roggero
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.