2017-08-10 15 views
0

我有一個API的服務器誰響應這樣的請求:困難下載文件時使用jQuery(GET)

http://localhost:8080/slim3/public/api/v1/files/Test1.jpg 
http://localhost:8080/slim3/public/api/v1/files/Test2.txt 
... 

如果我把這樣的URL到我的瀏覽器,我可以得到的下載提示。現在我正在努力通過jQuery/Ajax來處理文件的下載。 我在Stackoverflow上找到的每個線程都告訴我發回實際的下載url,並通過window.location打開它。我不明白這怎麼可能 當我的服務器已經爲我下載的文件,我只需要「抓住」它在客戶端?

很明顯,我無法通過jQuery/Javascript強制下載對話框。我在這裏閱讀了多個主題。但同樣的線程不會告訴我 我如何獲得直接下載url。或者不幸的是我在這裏混合了一些東西?

以下是我有:

客戶端(jQuery的)

$(document).ready(function(){ 
    $(document).on('click', '#file', function(e){ 
     e.preventDefault(); 
     var filename = $(this).data('url'); 
     $.ajax({ 
      type : "GET", 
      cache: false, 
      url : "http://localhost:8080/slim3/public/api/v1/files/" + filename, 
      success : function(data) { 
       console.log(data) // the console writes nothing 
       //window.location = "data:application/octet-stream," + encodeURIComponent(data); // not working    
       //var downloadUrl = data.url; // not working 
       //window.location = downloadUrl; // // not working 
      }, 
      error : function(data) {} 
     }); 
    }); 
}); 

服務器(PHP)

public function show($request, $response, $args) 
{    
    $file = 'C:\xampp\htdocs\slim3\storage\Test1.jpg'; 

    $res = $response->withHeader('Content-Description', 'File Transfer') 
       ->withHeader('Content-Type', 'application/octet-stream') 
       ->withHeader('Content-Disposition', 'attachment;filename="'.basename($file).'"') 
       ->withHeader('Expires', '0') 
       ->withHeader('Cache-Control', 'must-revalidate') 
       ->withHeader('Pragma', 'public') 
       ->withHeader('Content-Length', filesize($file)); 
    readfile($file); 
    return $res; 
} 

解決方案:

羅布指出我在正確的方向。我其實不需要做GET Ajax請求。因此,最終的jQuery函數如下exacty像這樣和工作原理:

$(document).on('click', '#file', function(e){ 
     e.preventDefault(); 
     var filename = $(this).data('url'); 
     window.location = "http://localhost:80/slimmi/public/api/v1/files/" + filename;   
    }); 
+0

你能解釋一下你真正想做什麼嗎?我真的不明白你在問什麼。 – lexith

+0

@lexith我試圖在我的客戶端上使用HTML/jQuery編寫的「另存爲」對話框。 – Magiranu

+0

好的,但你提到的建議解決方案有什麼問題?你說你知道你不能用ajax做。 – lexith

回答

1

您的服務器剛剛發回的名字在URL請求權的實際文件?

它看起來像你對我只是需要與

document.location =「http://localhost:8080/slim3/public/api/v1/files/」 +文件名替換所有Ajax代碼的;

您在PHP中設置的標題將確定瀏覽器是顯示保存對話框還是嘗試顯示文件 - 這些看起來是正確的。

+0

哇。我看不到樹木。我用解決方案更新了我的問題。謝謝! – Magiranu

2

在客戶端,filename變量將會出錯。它應該是Test1.jpgTest2.txt。我認爲$(this).data('url');返回當前的url而不是Test1.jpgTest2.txt的名字。你是否嘗試通過使用。減去文件名:

var url = $(this).data('url'); 
var filename = url.substring(url.lastIndexOf("/") + 1, url.length); 
1

如果這些文件是按需生成的,你可以做的是有PHP編碼爲Base64文件 - 像this,設置適當的類型 - 並返回給客戶。將Base64轉換爲Blob - 您可以使用將Base64置於anchor's href中,但IE的URI大小非常小 - 然後從該Blob創建一個URL對象。除此之外,這可確保數據的URL安全。最後,創建一個「隱形」錨點並點擊它。

$.ajax({ 
     type: "GET", 
     url: target, 
     success: function (response) { 
      // create a download anchor tag 
      var downloadLink = document.createElement('a'); 
      downloadLink.target = '_blank'; 
      downloadLink.download = 'your-file-name-here'; 

      // convert Base64 to Blob - don't forget to set content type! 
      var blob = b64toBlob(response, [file type here]); 

      // create an object URL from the Blob 
      var URL = window.URL || window.webkitURL; 
      var downloadUrl = URL.createObjectURL(blob); 

      // set object URL as the anchor's href 
      downloadLink.href = downloadUrl; 

      // append the anchor to document body 
      document.body.appendChild(downloadLink); 

      // fire a click event on the anchor 
      downloadLink.click(); 

      // cleanup: remove element and revoke object URL 
      document.body.removeChild(downloadLink); 
      URL.revokeObjectURL(downloadUrl); 
     } 
    }); 

將Base64轉換爲Blob像這樣 - source

function b64toBlob(b64Data, contentType, sliceSize) { 
    contentType = contentType || ''; 
    sliceSize = sliceSize || 512; 

    var byteCharacters = atob(b64Data); 
    var byteArrays = []; 

    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { 
     var slice = byteCharacters.slice(offset, offset + sliceSize); 

     var byteNumbers = new Array(slice.length); 
     for (var i = 0; i < slice.length; i++) { 
      byteNumbers[i] = slice.charCodeAt(i); 
     } 

     var byteArray = new Uint8Array(byteNumbers); 

     byteArrays.push(byteArray); 
    } 

    var blob = new Blob(byteArrays, {type: contentType}); 
    return blob; 
} 

這是我用來下載我們的Django服務器按需生成的PDF,她似乎運行得很好。

附錄

爲什麼我們的網站確實是這樣,而不是僅僅爲後續調用返回的文件名的原因是因爲它的服務器I/O上更容易一些。選擇的解決方案意味着請求的文件必須存在服務器上的某處 - 很可能在磁盤上。 (可以使用PHP的tmpfile()將生成的文件保存在內存中,但我對PHP的瞭解有限,所以我不知道如何在HTTP調用之間保留該文件)。

我的項目使得大量的PDF音頻 - 可能有數百頁。我真的不想讓這些數據中的實際文件對象保存到磁盤中,然後幾乎立即從磁盤上讀取它(我知道那不是,正好是服務器正在這樣做,但無論如何,你切片它做了比必要的更多的工作)。服務器上有PDF,它在內存中,爲什麼不把它還給客戶端?

返回這樣的文件意味着不需要做任何額外的清理工作 - 一旦Base64離開了建築物,就是這樣。沒有磁盤上的文件,因此沒有什麼必須稍後處理(取決於您的需要的好壞)。

+0

你爲什麼要這樣做,而不是直接發送PDF文件?我的意思是除了造成額外的流量 – lexith