之前做項目遇到這樣一個問題,就是在php環境下,用a標簽的href鏈接到一個資源,比如是mp3或者lrc文件時,點擊之后不是出現保存文件的提示,而是調用本地程序打開文件或者直接在瀏覽器上解析。網上說可以全部做成rar格式的文件,這個一方面不方便,有些情況下也不可能完全這樣做,還有實際上,做過測試會發現,在content-type:text/html的情況下,即時是rar有時也會被瀏覽器直接解析,無法實現下載的功能,那這個問題是不是就無解了呢?答案是否定的,幾番搜索+測試,終於發現了一個可行的解決方案,就是在點擊a標簽的鏈接之后,不是直接請求資源,而是對header做一下預處理,再去readfile就可以了。下面附上相關的測試代碼:
一、
index.php中:
<?php echo "<a href='process.php?filename=halo.mp3'>下載</a>" ?>
process.php中:
<?php header("Content-type: application/octet-stream"); header('Content-Disposition: attachment; filename="'. basename($_GET['filename']).'"'); header("Content-Length: ". filesize($_GET['filename'])); readfile($_GET['filename']); ?>
這是最簡單的方法,但是有個問題:如果請求的路徑中包含中文,那么下載的文件名有可能就是亂碼。
二、
針對上面問題的解決方案,
index.php中:
<?php echo "<a href='process.php?filename=halo光環.mp3'>下載</a>" ?>
process.php中:
<?php header("Content-type: application/octet-stream"); //處理中文文件名 $ua = $_SERVER["HTTP_USER_AGENT"]; $encoded_filename = urlencode($_GET['filename']); $encoded_filename = str_replace("+", "%20", $encoded_filename); if (preg_match("/MSIE/", $ua)) { header('Content-Disposition: attachment; filename="' . $encoded_filename . '"'); } else if (preg_match("/Firefox/", $ua)) { header("Content-Disposition: attachment; filename*=\"utf8''" . $_GET['filename'] . '"'); } else { header('Content-Disposition: attachment; filename="' . $_GET['filename'] . '"'); } header("Content-Length: ". filesize($_GET['filename'])); readfile($_GET['filename']); ?>
輸出的時候,如果是Apache+PHP,那么還需要發送到Apache的輸出緩沖區,最后才發送給用戶。而對於Nginx+fpm,如果它們分開部署的話,那還會帶來額外的網絡IO。
三、
現在貌似沒有問題了,但是readfile還是有問題的,雖然PHP的readfile嘗試實現的盡量高效,不占用PHP本身的內存,但是實際上它還是需要采用MMAP(如果支持),或者是一個固定的buffer去循環讀取文件,直接輸出。
那么能不能繞過PHP這層呢,直接由webserver把文件發送給用戶呢?可以的,我們可以使用Apache的module mode_xsendfile,讓Apache直接發送這個文件給用戶。
代碼實現如下:(process.php)
header("Content-type: application/octet-stream"); //處理中文文件名 $ua = $_SERVER["HTTP_USER_AGENT"]; $encoded_filename = urlencode($_GET['filename']); $encoded_filename = str_replace("+", "%20", $encoded_filename); if (preg_match("/MSIE/", $ua)) { header('Content-Disposition: attachment; filename="' . $encoded_filename . '"'); } else if (preg_match("/Firefox/", $ua)) { header("Content-Disposition: attachment; filename*=\"utf8''" . $_GET['filename'] . '"'); } else { header('Content-Disposition: attachment; filename="' . $_GET['filename'] . '"'); } //讓Xsendfile發送文件 header("X-Sendfile: $_GET['filename']");
最后,如果願意的話,可以先判斷后綴,因為有時候圖片當成文件下載也會引起一些不方便的:
$type = strrchr($_GET['filename'], "."); //獲取后綴 if($type == "jpg" || "png" || "gif"){ header("Content-Disposition: filename=$_GET['filename']"); //這里我試過,加引號的話,下載時會加到文件名中 header("Content-Type: image/$type"); }
轉自:http://m.blog.csdn.net/blog/nkliming/8536311