先上代码,看懂的可直接方便复制
public function download(){
$file_url = $_REQUEST('file_url'); //file_url可能是个url,也可能是文件相对路径
$file = '...对file_url进行处理找到文件绝对路径';
$file_realname = base64_decode($_REQUEST('file_realname')); //base64解码
$file_realname = rawurlencode($file_realname); //url编码
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);
}
}
分割线下面是详细原理和过程
-----------------------------------------------------------------------------------------------------------
最近的项目需求
实现文件上传(文件名为:≈你好;123.rar),且上传后要求立刻展示文件名,并可以点击文件名直接进行下载,点击确定提交保存到数据库。(说明:上传后不能立刻刷新页面,因为此时还未确定提交,而且服务器上的文件名已经是时间戳编码后的格式1526019720.rar)
需求分析
1.文件上传
2.上传后文件名展示
3.上传后文件直接下载
4.提交后,再次打开页面时的文件名展示
5.提交后,再次打开页面时的文件下载
解决方案
1.文件上传
上传的问题好解决,网上的js插件很多
2.上传后文件名展示
由于我们用的是php公共upload函数,上传后返回的时候直接就把文件名原原本本返回给js了,并没有进行html实体编码,所以文件名中有&特殊字符,直接展示会有乱码,必须对文件名进行实体编码,此时已经请求返回,没有新的请求,没办法执行php代码,只能用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;
}
-----重点来了------
3.上传后文件直接下载
由于服务器上的文件名已变成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在上传时重命名变成了时间戳格式),并且还要支持解码,所以就不能用md5这种,而且这里是文件上传后没有刷新页面,直接就可以下载,又不能用PHP的函数,只能是js,所以就想到了base64,参见:https://blog.csdn.net/u011127019/article/details/51673230,为了方便复制,我直接贴出代码:
//1.加密
var str = '124中文内容';
var base = new Base64();
var result = base.encode(str);
//document.write(result);
//2.解密
var result2 = base.decode(result);
document.write(result2);
function Base64() {
// private property
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// public method for encoding
this.encode = function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
}
// public method for decoding
this.decode = function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = _keyStr.indexOf(input.charAt(i++));
enc2 = _keyStr.indexOf(input.charAt(i++));
enc3 = _keyStr.indexOf(input.charAt(i++));
enc4 = _keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = _utf8_decode(output);
return output;
}
// private method for UTF-8 encoding
_utf8_encode = function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
// private method for UTF-8 decoding
_utf8_decode = function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
js生成的下载url大致如下
$('#download').attr('href','www.xxx.com/download?file_url='+res.data.src+'&file_realname='+new Base64().encode($('#file_realname').val()));
同时download函数中需要解码,最终的download函数为:
public function download(){
$file_url = $_REQUEST('file_url'); //file_url可能是个url,也可能是文件相对路径
$file = '...对file_url进行处理找到文件绝对路径';
$file_realname = base64_decode($_REQUEST('file_realname')); //base64解码
$file_realname = rawurlencode($file_realname); //url编码
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);
}
}
如果是POST,最好也用js进行base64编码,这样一个download函数就都支持了。
关于中文乱码,这里强烈建议前后端统一使用UTF8
4.提交后,再次打开页面时的文件名展示
数据库中我设置了两个字段,file_url和file_realname,这样就可以将文件路径和文件名分开保存,直接将原始文件名存入file_realname,因为是重新打开的页面,所以就可以使用php的htmlspecialchars函数进行实体编码了(如果要使用htmlspecialchars过滤单引号和双引号,还需加上第二个参数ENT_QUOTES)
5.提交后,再次打开页面时的文件下载
file_realname直接用base64_encode编码即可
<a style="color:#0b76e8" id="download" href="<?php echo (!empty($file_url)?"www.xxx.com/download?file_url=$file_url&file_realname=".base64_encode($file_realname):'');?>"><?php echo htmlspecialchars($file_realname);?></a>
终于写完了,文字纯手打+代码复制,如果觉得对你有些许帮助,求手抖给个赞
总结一下用到的编码
1.js 实体编码
2.php 实体编码(htmlspecialchars)
3.js base64编码
4.php base64编码+解码(base64_encode)
5.php url编码(rawurlencode)
6.本篇最关键的:
$file_realname = rawurlencode($file_realname);
header("Content-Disposition:attachment; filename = $file_realname; filename*=utf-8''$file_realname");