先上代碼,看懂的可直接方便複製php
public function download(){ $file_url = $_REQUEST('file_url'); //file_url參數多是個url,也多是文件相對路徑 $file = '...對$file_url進行處理找到文件絕對路徑'; $file_realname = $_REQUEST('file_realname'); //file_realname參數是js通過encodeURIComponent()函數編碼過的,提交以後,nginx通常都自動對url進行urldecode()解碼,因此直接接收就OK(若是發現沒自動解碼,就須要咱們手動urldecode) $file_realname = rawurlencode($file_realname); //由於file_realname要傳回瀏覽器,因此還須要url編碼,才能保證不亂嗎,且這裏應該用rawurlencode()而不是urlencode() if(!file_exists($file)){ echo "<script>alert('文件不存在')</script>"; return; }else{ header("Content-type:application/octet-stream"); header("Content-Disposition:attachment; filename = $file_realname; filename*=utf-8''$file_realname"); header("Accept-ranges:bytes"); header("Accept-length:".filesize($file)); readfile($file); } }
分割線下面是詳細原理和過程html
-----------------------------------------------------------------------------------------------------------html5
實現文件上傳(文件名爲:≈你好;123.rar),且上傳後要求馬上展現文件名(js插件在選擇完文件後默認會馬上調用文件上傳接口),並能夠點擊文件名直接進行下載,點擊肯定提交保存到數據庫。(說明:上傳後不能馬上刷新頁面,由於此時還未點擊肯定提交,並且服務器上的文件名已是時間戳編碼後的格式1526019720.rar)nginx
1.文件上傳數據庫
2.上傳後文件名展現後端
3.上傳後按原文件名直接下載瀏覽器
4.提交後,再次打開頁面時的原文件名展現服務器
5.提交後,再次打開頁面時按原文件名下載app
上傳的問題好解決,網上的js插件不少,這裏用的是Layui函數
因爲咱們項目裏用的是Layui的文件上傳插件,因此展現文件名須要本身處理,該插件中沒有對file對象的name屬性進行html實體編碼,因此若是文件名中有&等特殊字符(若是使用原生的<input type='file'>就不會有這個問題),直接展現會有亂碼,所以必須先對文件名file.name進行實體編碼(並且file對象是js生成的,沒有通過後端php,因此也只能用js對其進行html實體編碼),網上找到了js的html編碼相關函數:http://www.jb51.net/article/93623.htm,爲了方便複製,我直接貼出代碼:
function HTMLEncode ( input ) { var converter = document.createElement("DIV"); converter.innerText = input; var output = converter.innerHTML; converter = null; return output; } function HTMLDecode ( input ) { var converter = document.createElement("DIV"); converter.innerHTML = input; var output = converter.innerText; converter = null; return output; }
-----重點來了------
因爲服務器上的文件名已變成1526019720.rar,因此要使用url直接下載的話,下載後文件名也會是1526019720.rar,因此只能去請求PHP文件,用PHP設置header的方式改變文件名,並把文件路徑和文件名經過參數傳給PHP文件(此時還沒辦法只傳個id,經過id查詢數據庫獲取文件路徑和文件名,由於尚未提交到數據庫!!!),參數傳遞一種是GET,一種是POST(強烈建議使用POST方式,這樣就省去了對url進行編碼,若是使用GET方式...大家懂的,下面會講到使用GET方式)
先講一下使用php的header方式進行文件下載,通常相似以下這種方式
<?php public function download(){ $file = '....具體的文件路徑'; $file_realname = '....具體的文件名'; header("Content-type:application/octet-stream"); header("Content-Disposition:attachment; filename = $file_realname;) header("Accept-ranges:bytes"); header("Accept-length:".filesize($file)); readfile($file); }
可是使用這種寫法,會發現若是文件名中有特殊字符(好比;等),則下載後的文件名會顯示不正確,由於「;」分號是 header("Content-Disposition:attachment; filename = $file_realname;)的分隔符
通過一番查找,終於找到一篇博客解惑了:https://www.cnblogs.com/hihtml5/p/7220188.html?utm_source=itdadao&utm_medium=referral
最終肯定使用php的自帶函數rawurlencode(),並把Content-Disposition:attachment改成:
$file_realname = rawurlencode($file_realname); header("Content-Disposition:attachment; filename = $file_realname; filename*=utf-8''$file_realname");
接下來就是使用GET方式傳遞文件路徑和文件名給這個PHP函數,由於文件名中有「&」符號,url中&是有特殊意義的,因此必須進行url編碼(這裏只須要對file_realname編碼便可,file_url在上傳時重命名變成了時間戳格式),並且這裏是文件上傳後沒有刷新頁面,直接就能夠下載,所以不能用PHP的函數,只能是js端使用encodeURIComponent()進行編碼(由於是url局部編碼,不能用encodeURI(),參見:http://www.w3school.com.cn/jsref/jsref_encodeURIComponent.asp)
特別說明:以前剛開始寫這篇博客的時候,說的是js端用base64編碼方式對file_realname編碼,後來發現是有瑕疵的,由於base64編碼後有可能會產生+等特殊符號,url提交後得到的參數是空格,所以,在這裏特別更正一下,仍是要用標準的url局部編碼encodeURIComponent()
js生成的下載url大體以下
$('#download').attr('href','www.xxx.com/download?file_url='+res.data.src+'&file_realname='+new Base64().encode($('#file_realname').val()));
通常狀況下,nginx等Web服務器都會自動對url解碼(不管是get仍是post),因此php端無需手動解碼,最終的download函數爲:
public function download(){ $file_url = $_REQUEST('file_url'); //file_url參數多是個url,也多是文件相對路徑 $file = '...對$file_url進行處理找到文件絕對路徑'; $file_realname = $_REQUEST('file_realname'); //file_realname參數是js通過encodeURIComponent()函數編碼過的,提交以後,nginx通常都自動對url進行urldecode()解碼,因此直接接收就OK(若是發現沒自動解碼,就須要咱們手動urldecode) $file_realname = rawurlencode($file_realname); //由於file_realname要傳回瀏覽器,因此還須要url編碼,才能保證不亂嗎,且這裏應該用rawurlencode()而不是urlencode() if(!file_exists($file)){ echo "<script>alert('文件不存在')</script>"; return; }else{ header("Content-type:application/octet-stream"); header("Content-Disposition:attachment; filename = $file_realname; filename*=utf-8''$file_realname"); header("Accept-ranges:bytes"); header("Accept-length:".filesize($file)); readfile($file); } }
關於中文亂碼,這裏強烈建議先後端統一使用UTF8
數據庫中我設置了兩個字段,file_url和file_realname,這樣就能夠將文件路徑和文件名分開保存,直接將原始文件名存入file_realname,由於是從新打開的頁面,因此就可使用php的htmlspecialchars函數進行實體編碼了(若是要使用htmlspecialchars過濾單引號和雙引號,還需加上第二個參數ENT_QUOTES)
file_realname直接用rawurlencode編碼便可
<a style="color:#0b76e8" id="download" href="<?php echo (!empty($file_url)?"www.xxx.com/download?file_url=$file_url&file_realname=".rawurlencode($file_realname):'');?>"><?php echo htmlspecialchars($file_realname);?></a>
終於寫完了,文字純手打+代碼複製,若是以爲對你有些許幫助,求手抖給個贊
1.js 實體編碼
2.php 實體編碼(htmlspecialchars)
3.js url編碼(encodeURIComponent())
4.php url編碼(rawurlencode)
5.本篇最關鍵的:
$file_realname = rawurlencode($file_realname); header("Content-Disposition:attachment; filename = $file_realname; filename*=utf-8''$file_realname");