php 创建下载链接和中文文件名乱码解决
2012-08-18 21:30
399 查看
Creatingadownloadlink
Aquestionthatcropsup(突然出现)regularlyinonlineforumsis,HowdoIcreatealinktoanimage
(orPDFfile)thatpromptstheusertodownloadit?Thequicksolutionistoconvertthefile
intoacompressedformat,suchasZIP.Thisfrequentlyresultsinasmallerdownload,but
thedownside负面isthatinexperiencedusersmaynotknowhowtounzipthefile,ortheymay
beusinganolderoperatingsystemthatdoesn’tincludeanextractionfacility.WithPHPfile
systemfunctions,it’seasytocreatealinkthatautomaticallypromptstheusertodownload
afileinitsoriginalformat.ThescriptsendsthenecessaryHTTPheaders,opensthe
file,andoutputsitscontentsasabinarystream.
Ifyouwanttheusertobepromptedtosavethedatayouaresending,suchasageneratedPDFfile,youcanusethe»Content-Dispositionheadertosupplyarecommendedfilenameandforcethebrowsertodisplaythesavedialog.
某一类型文件:
readfile—Outputsafile
fpassthru—Outputallremainingdataonafilepointer
---------------------------------------------------------------------
我在文件1里面写了如何代码:
说明一点,filename能含有空格,最好用双引号括起来。
文件名是因为ok,但是中文,如“中文名测试.jpg”下载的文件名就是jpg.怎么回事。
想了下,只能是basename出了问题。
果不其然。
输出:
../../images/中文名测试.jpg
.jpg
可以看到,basename不支持中文。发现如果是中文的文件名返回只有后缀的空文件名(如:.pdf)
stringbasename(stringpath[,stringsuffix])
说明
给出一个包含有指向一个文件的全路径的字符串,本函数返回基本的文件名。如果文件名是以suffix结束的,那这一部分也会被去掉。
Aquestionthatcropsup(突然出现)regularlyinonlineforumsis,HowdoIcreatealinktoanimage
(orPDFfile)thatpromptstheusertodownloadit?Thequicksolutionistoconvertthefile
intoacompressedformat,suchasZIP.Thisfrequentlyresultsinasmallerdownload,but
thedownside负面isthatinexperiencedusersmaynotknowhowtounzipthefile,ortheymay
beusinganolderoperatingsystemthatdoesn’tincludeanextractionfacility.WithPHPfile
systemfunctions,it’seasytocreatealinkthatautomaticallypromptstheusertodownload
afileinitsoriginalformat.ThescriptsendsthenecessaryHTTPheaders,opensthe
file,andoutputsitscontentsasabinarystream.
<?php //blockanyattempttoexplorethefilesystem if(isset($_GET['file']&&basename($_GET['file'])==$_GET['file']){ $getfile=$_GET['file']; } else{ $getfile=NULL; } //defineerrorhandling $nogo='Sorry,downloadunavailable.<ahref="prompt.php">Back</a>.'; if(!$getfile){ //gonofurtheriffilenamenotset echo$nogo; } else{ //definethepathnametothefile $filepath='C:/htdocs/phpsolutions/images/'.$getfile; //checkthatitexistsandisreadable if(file_exists($filepath)&&is_readable($filepath)){ //getthefile'ssizeandsendtheappropriateheaders $size=filesize($filepath); header('Content-Type:application/octet-stream'); header('Content-Length:'.$size); header('Content-Disposition:attachment;filename='.$getfile); header('Content-Transfer-Encoding:binary'); //openthefileinbinaryread-onlymode //suppresserrormessagesifthefilecan'tbeopened $file=@fopen($filepath,'rb'); if($file){ //streamthefileandexitthescriptwhencomplete fpassthru($file); exit; } else{ echo$nogo; } } else{ echo$nogo; } } ?>
Content-Disposition很重要,去掉后不行,一般IE有提示,chrome直接下载。 也可以用echofread($file,filesize($file_dir.$file_name));fclose($file)来获取。
Ifyouwanttheusertobepromptedtosavethedatayouaresending,suchasageneratedPDFfile,youcanusethe
某一类型文件:
<?php //We'llbeoutputtingaPDF header('Content-type:application/pdf'); //Itwillbecalleddownloaded.pdf header('Content-Disposition:attachment;filename="downloaded.pdf"'); //ThePDFsourceisinoriginal.pdf readfile('original.pdf'); ?>
readfile—Outputsafile
fpassthru—Outputallremainingdataonafilepointer
---------------------------------------------------------------------
我在文件1里面写了如何代码:
<body> <h1>下载测试</h1> <ahref="downLoad.php?file=中文名测试.jpg">点击下载</a> </body>
downLoad.php内容如下:
<?php if(isset($_GET['file'])) { define("DIR",'../../images/'); $fileName=$_GET['file']; $fileName=DIR.$fileName; header("Content-type:application/octet-stream");//octet八位字节octet-stream字节流 header('Content-disposition:attachment;filename="'.basename($fileName).'"'); header("Content-length:".filesize($fileName)); readfile($fileName); } ?>
说明一点,filename能含有空格,最好用双引号括起来。
文件名是因为ok,但是中文,如“中文名测试.jpg”下载的文件名就是jpg.怎么回事。
想了下,只能是basename出了问题。
果不其然。
<?php define("DIR",'../../images/'); $fileName=DIR.'中文名测试.jpg'; echo$fileName; echo'<br/>'.basename($fileName); ?>
输出:
../../images/中文名测试.jpg
.jpg
可以看到,basename不支持中文。发现如果是中文的文件名返回只有后缀的空文件名(如:.pdf)
stringbasename(stringpath[,stringsuffix])
说明
给出一个包含有指向一个文件的全路径的字符串,本函数返回基本的文件名。如果文件名是以suffix结束的,那这一部分也会被去掉。
按照网站上找到说法是此函数依赖于区域设置,如果是多字节名称返回为空可以通过setlocale函数如下设置
1 2 3 4 | <?php setlocale(LC_ALL, 'zh_CN.GBK' ); //oranyotherlocalethatcanhandlemultibytecharacters. ?> |
从网上看到一篇文章:
PHPbasename函数linux下中文路径的问题解决方法
basename函数用来获取路径中文件名部分,
可是在redhat4.3或者某些其他系统下,无法正确获得带有中文的路径中的文件名,
下面这个函数来自网上,很好很强大,可以代替basename使用:
functionsbasename($filename){
returnpreg_replace('/^.+[\\\\\\/]/','',$filename);
}
如果linux系统不能正确显示中文目录和文件名,可以在/etc/profile的最后加入:
exportLANG="zh_CN.GBK"
至于为什么没用UTF-8,因为中文windows系统的文件名似乎是gbk的,
用utf-8编码的文件名在windows系统下会显示为乱码。所以要用gbk的编码去写文件、目录名。
linux下也设置成gbk,就和windows服务器上的处理一致了。
本身php程序和数据库都是用了utf8的字符集,所以在处理文件名的时候需要来回转码,顺便贴一个比较安全的转码函数:
functionsiconv($str,$out_charset,$in_charset){
if(strtoupper($out_charset)!=strtoupper($in_charset)){
if(function_exists('iconv')&&(@$outstr=iconv("$in_charset//IGNORE","$out_charset//IGNORE",$str))){
return$outstr;
}elseif(function_exists('mb_convert_encoding')&&(@$outstr=mb_convert_encoding($str,$out_charset,$in_charset))){
return$outstr;
}
}
return$str;
}
声明:文章转载自
我换成sbasename函数,果然就ok了。basename正确输出"中文名测试.jpg".
但是无语的是在chrome下没问题,但在Ie下文件名乱码了。看了php鸟哥的那篇文章:
让PHP更快的提供文件下载
一般来说,我们可以通过直接让URL指向一个位于DocumentRoot下面的文件,来引导用户下载文件.但是,这样做,就没办法做一些统计,权限检查,等等的工作.于是,很多时候,我们采用让PHP来做转发,为用户提供文件下载.
<?php
$file="/tmp/dummy.tar.gz";
header("Content-type:application/octet-stream");
header('Content-Disposition:attachment;filename="'.basename($file).'"');
header("Content-Length:".filesize($file));
readfile($file);
但是这个有一个问题,就是如果文件是中文名的话,有的用户可能下载后的文件名是乱码.
于是,我们做一下修改(参考::
<?php
$file="/tmp/中文名.tar.gz";
$filename=basename($file);
header("Content-type:application/octet-stream");
//处理中文文件名
$ua=$_SERVER["HTTP_USER_AGENT"];
$encoded_filename=rawurlencode($filename);
if(preg_match("/MSIE/",$ua)){
header('Content-Disposition:attachment;filename="'.$encoded_filename.'"');
}elseif(preg_match("/Firefox/",$ua)){
header("Content-Disposition:attachment;filename*=\"utf8''".$filename.'"');
}else{
header('Content-Disposition:attachment;filename="'.$filename.'"');
}
header("Content-Length:".filesize($file));
readfile($file);
恩,现在看起来好多了,不过还有一个问题,那就是readfile,虽然PHP的readfile尝试实现的尽量高效,不占用PHP本身的内存,但是实际上它还是需要采用MMAP(如果支持),或者是一个固定的buffer去循环读取文件,直接输出.
输出的时候,如果是Apache+PHPmod,那么还需要发送到Apache的输出缓冲区.最后才发送给用户.而对于Nginx+fpm如果他们分开部署的话,那还会带来额外的网络IO.
那么,能不能不经过PHP这层,直接让Webserver直接把文件发送给用户呢?
今天,我看到了一个有意思的文章:
我们可以使用Apache的module
<?php
$file="/tmp/中文名.tar.gz";
$filename=basename($file);
header("Content-type:application/octet-stream");
//处理中文文件名
$ua=$_SERVER["HTTP_USER_AGENT"];
$encoded_filename=rawurlencode($filename);
if(preg_match("/MSIE/",$ua)){
header('Content-Disposition:attachment;filename="'.$encoded_filename.'"');
}elseif(preg_match("/Firefox/",$ua)){
header("Content-Disposition:attachment;filename*=\"utf8''".$filename.'"');
}else{
header('Content-Disposition:attachment;filename="'.$filename.'"');
}
//让Xsendfile发送文件
header("X-Sendfile:$file");
X-Sendfile头将被Apache处理,并且把响应的文件直接发送给Client.
Lighttpd和Nginx也有类似的模块,大家有兴趣的可以去找找看。
解决乱码的关键是判断useragent,这篇文章仔细说了原因:
我按照以上的:
改成:
<?php
if(isset($_GET['file']))
{
define("DIR",'../../images/');
$fileName=$_GET['file'];
$file=DIR.$fileName;
functionOwnbasename($filename)
{
returnpreg_replace('/^.+[\\\\\\/]/','',$filename);
}
header("Content-type:application/octet-stream");//octet八位字节octet-stream字节流
$fileName=Ownbasename($file);
$ua=$_SERVER['HTTP_USER_AGENT'];
$encoded_fileName=rawurlencode($fileName);
if(preg_match("/MSIE/",$ua))
{
header('Content-Disposition;attachment;filename="'.$encoded_fileName.'"');
}
elseif(preg_match("/Firefox/",$ua))
{
header("Content-Disposition:attachment;filename*=\"utf8''".$fileName.'"');
}
else
{
header('Content-Disposition:attachment;filename="'.$fileName.'"');
}
header("Content-Length:".filesize($file));
readfile($file);
}
?>
结果在Ie下报错:
b>Warning</b>:filesize():statfailedfor../../images/中文名测试.txtin<b>F:\xampp\htdocs\php\download\downLoad.php</b>online<b>33</b><br/>
<br/>
<b>Warning</b>:readfile(../../images/中文名测试.txt):failedtoopenstream:Invalid
Ie下用
header('Content-Disposition后filesize()和readfile读取不了中文文件名了。当我注释掉header判断,还是用原来的:
header('Content-Disposition:attachment;filename="'.$fileName.'"');
虽然乱码,但可以下载文件。
还没解决这个问题。
使用PHP创建ZIP格式的下载压缩包
相关文章推荐
- 解决PHP在IE浏览器下载文件,中文文件名乱码问题
- 跨浏览器PHP下载文件名中的中文乱码问题解决方法
- 解决PHP下载文件名中文解决乱码
- 解决PHP Header强制下载IE文件名中文乱码问题
- 解决php 文件下载 IE 文件名中文乱码
- php中强制下载文件的代码(解决了IE下中文文件名乱码问题)
- 跨浏览器PHP下载文件名中的中文乱码问题解决方法
- 解决PHP在IE浏览器下载文件,中文文件名乱码问题
- 跨浏览器PHP下载文件名中的中文乱码问题解决方法
- 解决跨浏览器下PHP下载文件名中的中文乱码问题
- 彻底解决跨浏览器下PHP下载文件名中的中文乱码问题
- php中强制下载文件的代码(解决了IE下中文文件名乱码问题)
- 解决PHP在IE中下载文件,中文文件名乱码问题
- 解决php输出文件下载时文件名含中文时出现乱码
- 【整理】解决php输出文件下载时文件名含中文时出现乱码
- 解决不同浏览器上中文文件名的下载乱码问题php验证可行的测试过ie ff 和chrome的
- PHP导出EXCEL快速开发指南--PHPEXCEL的使用详解,解决PHP下载文件名中文乱码
- 解决PHP在IE浏览旗下载文件,中文文件名乱码问题
- Firefox、IE等浏览器下载文件名中文乱码的一种Server端解决方法
- php读取目录中文文件名乱码解决方法