PHP - Windows - 上传后文件名不正确(ü 另存为 ü 等)

PHP - Windows - filename incorrect after upload (ü saved as ü etc.)

我有这个允许多个文件上传的自制应用程序,我使用 AJAX 将文件传递给 php,使用 php 创建新目录,将上传的文件移到那里并保存数据库的目录位置。然后查看文件我运行列出保存在db中的目录位置

问题是来自世界各地的文件经常包含一些非拉丁字符,例如 ü。当我回显 php 中的文件名时,即使名称是用阿拉伯语编写的名称,名称也会正确显示,但它们正在使用编码名称保存在服务器上,例如 Ï 代替 ü。当我列出目录中的文件时,我可以看到名称 ü.txt insted of ü.txt 但是当我单击它时服务器 returns 找不到错误对象(因为在服务器上它被保存为 ü.txt 并且它将 link 读取为 ü.txt)。

我尝试了一些建议的解决方案,例如使用 iconv,但文件名仍然以相同的方式保存。

我可以发誓当 Web 应用程序托管在 linux 上时问题不存在,但目前我不再那么确定了。现在我暂时 运行 它在 xampp 上(在 Windows 上)并且似乎文件名是使用 windows-1252 编码保存的(默认 Windows' 编码在服务器)。是不是默认Windows编码相关的问题?

老实说,我不知道如何解决这个问题,如果有任何帮助,我将不胜感激。我应该继续尝试以不同的字符编码保存文件,还是以不同的方式处理它并更改列出已保存和编码的文件的方式会更好?

编辑。根据(最终)关闭的 bug report 它已在 php 7.1.

中修复

您是否使用 $_FILES['upfile']['name'] 命名文件?这可能会造成你的问题。

使用 GNU Recode 怎么样?

$fileName = recode_string('latin1',$_FILES['upfile']['name']);

语法:

recode_string(string recode type,string $string)

有效字符集:http://www.faqs.org/rfcs/rfc1345.html

您必须以某种方式验证上传文件名中的字符。

你也可以试试sprintf。格式化字符串字符可能无法预测,但可能会起作用。

$fileName = pathinfo($_FILES['upfile']['name'], PATHINFO_FILENAME);
$fileName = sprintf('./uploads/%s',$fileName);

保存文件名时使用mysql_escape_string();

$fileName = mysql_escape_string($fileName);

对于纳粹喜欢告诉我的句法语法,我已经贬低了,好像我还不知道。

mysqli_real_escape_string()

请注意 mysqli 语法纳粹 喜欢对我的答案投反对票,因为我使用 mysql 而不是 msqli,享受生活。我一直在编写代码,因为它是在带有纸质打孔卡的电传打字机上完成的。早在你出生之前。

最后我用下面的方法解决了:

  1. 上传文件时,我url使用rawurlencode()
  2. 对名称进行编码
  3. 从服务器获取文件时,它们显然是 URL 编码的,所以我使用 urldecode($filename) 打印正确的名称
  4. a href 中的链接是自动翻译的,例如“%20”变成了“”,URL 最终不正确,因为它链接到不正确的文件名。我决定对它们进行编码并打印它们,最后是这样的:print $dirReceived.rawurlencode($file);($dirReceived 是存储接收文件的目录,在代码的前面定义)
  5. 我还添加了带有 urldecode($filename) 的下载属性,以便在需要时使用 UTF-8 名称保存文件。

多亏了这个,我在服务器上保存了带有 url 编码名称的文件。可以在浏览器中打开它们(非常重要,因为它们大部分是 *.pdf)并且可以使用正确的名称下载它们,这让我可以上传和下载名称以阿拉伯语、西里尔语等书写的文件。

到目前为止我已经测试过了,看起来不错。我正在考虑在生产代码中实现它。上面有 concerns/thoughts 吗?

编辑。

因为没有人反对,我 select 我的回答是解决我问题的答案。在做了一些测试之后,客户端和服务器端的一切看起来都很好。在服务器上保存文件时,它们是 URL 编码的,下载它们时,它们被解码并以正确的名称保存。

一开始我使用的代码是:

    for($i=0;$i<count($_FILES['file']['name']);$i++) 
{
    move_uploaded_file($_FILES['file']['tmp_name'][$i],
    "../filepath/" . $_FILES['file']['name'][$i]);
}

此方法导致保存文件时出现问题,并将每个 UTF-8 特殊字符替换为 cp1252 编码的字符(ü 另存为 ϼ 等),因此我添加了一行并将该代码替换为以下代码:

for($i=0;$i<count($_FILES['file']['name']);$i++) 
{
    $fname= rawurlencode($_FILES['file']['name'][$i]);
    move_uploaded_file($_FILES['file']['tmp_name'][$i],
    "../filepath/" . $fname);
}

这允许我使用与 cp1252 和 UTF-8 兼容的 URL 编码(% 和两个十六进制)在服务器上保存任何文件名。

为了列出保存的文件,我使用我保存在数据库中的文件路径并列出文件。我正在使用以下代码:

    if (is_dir($dir)){
  if ($dh = opendir($dir)){
    while (($file = readdir($dh)) !== false){
        if(is_file($dir . $file)){

    echo "<li><a href='".$dir.$file."' download='".$file ."'>".$file."</a></li><br />";

    }
}
    closedir($dh);
  }
}

由于 URL 编码的文件名被自动解码,我将其更改为:

    if (is_dir($dir)){
  if ($dh = opendir($dir)){
    while (($file = readdir($dh)) !== false){
        if(is_file($dir . $file)){
            echo "<li><a href='";
            print $dir.rawurlencode($file);
            echo "' download='" . urldecode($file) ."'>".urldecode($file)."</a></li><br />";
    }
}
    closedir($dh);
  }
}

我不知道这是否是解决问题的最佳方法,但效果很好,而且我知道通常最好不要使用 php 生成 html 标签但目前我有一些严重的错误需要首先解决,然后我将不得不处理代码本身的外观。

编辑2

还有一个好处是我不必更改已上传文件的名称,这对我来说是一个很大的优势。