我有一个自制的应用程序,允许多个文件上传,我使用 AJAX 将文件传递给 php,使用 php 创建新的目录,移动到那里上传的文件并将目录位置保存到数据库。然后要查看我运行的文件,列出了保存在数据库中的目录位置。
问题是文件来自世界各地,因此它们经常包含一些非拉丁字符,例如 ü。当我回显 php 名称中的文件名时,即使它们的名称是用阿拉伯语写的,它们也会正确显示,但它们以编码名称保存在服务器上,例如 Ã1/4 代替 ü。当我列出目录中的文件时,我可以看到名称 ü.txt 而不是 Ã1/4.txt但是当我单击它时,服务器返回错误对象未找到(因为在服务器上它被保存为 Ã1/4.txt并将链接读取为 ü.txt)。
我尝试了一些建议的解决方案,例如使用 iconv,但文件名仍然以相同的方式保存。
我可以发誓,当 Web 应用程序托管在 Linux 上时,问题不存在,但目前我不再那么确定了。现在我暂时在 xampp(在 Windows 上)上运行它,似乎文件名是使用 windows-1252 编码(服务器上的默认 Windows 编码)保存的。是默认的Windows编码相关问题吗?
老实说,我不知道如何处理这个问题,我将不胜感激任何帮助。我应该继续尝试以不同的字符编码保存文件,还是以不同的方式处理它并更改列出已保存和编码文件的方式会更好?
编辑。根据(最终)关闭的错误报告,它在php 7.1中得到了修复。
最后我用以下方法解决了它:
- 上传文件时,我用
rawurlencode()
对名称进行编码 - 从服务器获取文件时,它们显然是 URL 编码的,所以我使用
urldecode($filename)
打印正确的名称 a href
中的链接会自动翻译,因此例如"%20"变为",并且URL最终不正确,因为它链接到不正确的文件名。我决定将它们编码回来并打印出来,最终得到这样的内容:print $dirReceived.rawurlencode($file);
($dirReceived是存储收到的文件的目录,在代码的前面定义)- 我还添加了带有
urldecode($filename)
的下载属性,以在需要时使用 UTF-8 名称保存文件。
多亏了这一点,我将带有 url 编码名称的文件保存在服务器上。可以在浏览器中打开它们(非常重要,因为它们中的大多数都是 *.pdf),并且可以使用正确的名称下载它们,这让我甚至可以上传和下载名称用阿拉伯语、西里尔文等书写的文件。
到目前为止,我测试了它,看起来不错。我正在考虑在生产代码中实现它。对此有什么顾虑/想法吗?
编辑。
由于没有异议,我选择我的答案作为解决我问题的答案。在进行了一些测试后,客户端和服务器端的一切看起来都不错。将文件保存在服务器上时,它们是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 编码字符(ü 保存为 Ã1/4 等),因此我添加了一行并将该代码替换为以下内容:
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);
}
这允许我使用 URL 编码(% 和两个十六进制)在服务器上保存任何文件名,该编码与 cp1252 和 UTF-8 兼容。
为了列出保存的文件,我使用保存在数据库中的文件路径并列出它们作为文件。我使用以下代码:
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
同样很棒的是我不必更改已上传文件的名称,这对我来说是一个很大的优势。
您是否使用$_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);
保存文件名时使用
$fileName = mysqli_real_escape_string($fileName)