可以';t下载具有特殊字符的文件,如“æøå";使用我的php下载脚本


Can't download files with special character like "æøå" with my php download script

我的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/Exempel påadmin.txt"

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

阵列([file]=>/Exempel påadmin.txt)files/projects/Abrahallen/administrasjon/Exempel påadmin.txt
警告:filesize():在C:''examplep''htdocs''files.php16
行,对文件/projects/Abrahallen/administrajion/Exempel påadmin.txt的stat失败
警告:readfile(files/projects/Abrahallen/administrasjon/Exempel påadmin.txt):无法打开流:在17行的C:''examplep''htdocs''files.php中没有这样的文件或目录

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

类似:

$getFile = $_SESSION['base'].$_GET['file'];
$getFile = realpath($getFile);
// This may or may not be needed... 
$getFile = mb_convert_encoding($getFile, "UTF-8");
$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或OSX服务器上,则可以通过这种方式访问所有文件名。然而,在Windows服务器上,文件系统本身就是Unicode,当您使用字节字符串访问它时(就像PHP和其他使用标准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脚本访问本地文件名确实很难安全地完成,如果有任何方法可以避免,你应该这样做。例如,如果您自己编写文件名(而不是为现有的文件目录提供服务),那么您可以应用自己的特殊编码(例如hex-encoded-UTF-8)来避免棘手的字符。或者使用存储在数据库中的文件ID。

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

正确使用这个参数也是一件痛苦的事。有关详细信息,请参阅此问题。