用我的php下载脚本无法下载具有特殊字符的文件,如“æøå”

时间:2015-12-30 15:31:55

标签: php string unicode encoding

我遇到问题,让我的PHP下载脚本使用特殊的字母字符,例如“æøå”,这些字符不在英文字母表中。无法找到包含这些字母的文件,我想知道是否存在某种编码问题。这些文件存储在运行XAMPP的Windows机器上。

$getFile = $_SESSION['base'].$_GET['file'];
$getFile = mb_convert_encoding($getFile, "UTF-8");

if (file_exists($getFile)) { //Retrives the file in path $getFile
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="'.basename($getFile).'"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($getFile));
    readfile($getFile);
    exit;
}

分配给$ getFile的字符串可能如下所示“files / projects / Abrahallen / administrasjon /Exempelpåadmin.txt”

因此,当请求具有特殊字符的文件名时,file_exists找不到文件,如果我注释掉if语句,则会收到此错误消息

阵 (     [file] => /Exempelpåadmin.txt ) 文件/项目/ Abrahallen / administrasjon /Exempelpåadmin.txt
警告:filesize():对于 C:\ xampp \ htdocs \ files.php 中的文件/项目/ Abrahallen / administrasjon /Exempelpåadmin.txt,stat失败< b> 16

警告:readfile(files / projects / Abrahallen / administrasjon /Exempelpåadmin.txt):无法打开流: C:\ xampp \ htdocs \ files.php中没有此类文件或目录在行 17

2 个答案:

答案 0 :(得分:0)

在生成文件路径时尝试使用realpath

喜欢:

$getFile = $_SESSION['base'].$_GET['file'];
$getFile = realpath($getFile);
// This may or may not be needed... 
$getFile = mb_convert_encoding($getFile, "UTF-8");

答案 1 :(得分:0)

$getFile = $_SESSION['base'].$_GET['file'];

首先这是危险的。文件名可以包含..之类的序列,这些序列将转义此目录,允许访问服务器上的任何文件,而不仅仅是base目录中的文件。此文件路径需要强大的验证。

$getFile = mb_convert_encoding($getFile, "UTF-8");

这可能不是正确的事情。您正在从internal_encoding将字符串转换为UTF-8。这可能是UTF-8(在这种情况下,它什么都不做),或者它可能是环境定义的(在这种情况下,它不可靠,并且在部署到不同的服务器时会中断)。无论哪种方式,你最终会得到一个与你输入的字符串不同的字符串,这与文件系统上的内容不匹配,因此找不到文件。

所以摆脱这一行,你将file参数视为一系列简单的字节。如果您自己生成指向脚本的链接(例如,使用scandir()列出文件并通过附加'?file='.urlencode($filename)创建指向它们的链接),那么这将没问题。

好吧,大多数都很好。如果脚本部署在Linux或OS X服务器上,您将能够以这种方式访问​​所有文件名。但是在Windows服务器上,文件系统本身是Unicode,当您使用字节字符串(如PHP和其他使用标准C stdio接口的应用程序)访问它时,Windows使用'ANSI将这些字节转换为Unicode '代码页,总是一些糟糕的遗留区域设置特定编码,而不是UTF-8。

因此,在西方(ANSI代码页1252)Windows安装中,您将能够访问Exempel på admin.txt,但由于以下原因您无法访问Příklady admin.txt非西方人物。此外,当您将服务移动到其他服务器时,URL的含义可能会发生变化。例如,如果您从Windows服务器转到Linux服务器,或者从西方Windows服务器转到中文服务器,那么file参数的隐式编码将会改变,而具有非ASCII字符的旧链接将会中断。

通常,更好的处理方法是将参数视为始终为UTF-8,并使用Windows自己的Unicode本机函数而不是C标准库来访问文件系统。不幸的是,PHP没有内置调用这些函数的能力,所以这很难做到。

通常,从PHP脚本访问本地文件名很难安全地进行,如果有任何方法可以避免它,你应该这样做。例如,如果您自己编写文件名(而不是提供现有的文件目录),那么您可以应用自己的ad-hoc编码(例如hex-encoded-UTF-8)来避免棘手的字符。或者使用存储在数据库中的文件ID。

header('Content-Disposition: attachment; filename="'.basename($getFile).'"');

正确获取此参数也是一种痛苦。有关详细信息,请参阅this question