【问题标题】:Can't download files with special character like "æøå" with my php download script无法使用我的 php 下载脚本下载具有特殊字符(如“æøå”)的文件
【发布时间】:2016-04-04 14:20:48
【问题描述】:

我在让我的 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 语句,我会收到此错误消息

数组 ( [文件] => /Exempel på admin.txt ) files/projects/Abrahallen/administrasjon/Exempel på admin.txt
警告:filesize(): stat failed for files/projects/Abrahallen/administrasjon/Exempel på admin.txt in C :\xampp\htdocs\files.php16

警告:readfile(files/projects/Abrahallen/administrasjon/Exempel på admin.txt): 无法打开流: C:\xampp\htdocs\files.php 中没有这样的文件或目录 17

【问题讨论】:

  • 您的问题到底是什么?请更具体,如果有错误消息,请添加。
  • 那么脚本找不到带有“æøå”之类字母的文件,但会找到不包含这些字符的其他文件

标签: php string unicode encoding


【解决方案1】:

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

喜欢:

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

【讨论】:

  • 那行不通。我知道该文件的路径是正确的,因为它可以下载该目录中不包含特殊字符的所有其他文件。
  • this question 的答案可能对您有所帮助。如果您通过服务器上的脚本存储这些文件,那么您应该考虑按照链接的答案中的建议对文件名进行编码。
【解决方案2】:
$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-native 函数而不是 C 标准库来访问文件系统。不幸的是 PHP 没有能力调用这些内置函数,所以这很棘手。

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

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

正确设置这个参数也是一件很痛苦的事情。详情请见this question

【讨论】:

  • 感谢您的详细解答。您能否推荐任何其他方式,如果他们没有通过 Web 界面访问它的适当权限,我可以为具有不同级别访问权限的用户授予对其他人限制的目录的访问权限?因为似乎 php 缺乏安全方便的方法。
  • 是的,此时您将希望在数据库中存储用户列表以及他们有权访问的存储,提供登录界面,并在允许 readfile 之前检查数据库中的授权继续。
猜你喜欢
  • 2018-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-03
  • 1970-01-01
相关资源
最近更新 更多