【问题标题】:UTF-8 characters in uploaded file name are jumbled on file upload上传文件名中的 UTF-8 字符在文件上传时混乱
【发布时间】:2013-12-22 21:18:51
【问题描述】:

我在 IIS7 上运行系统。页面 META 标记的编码为 UTF-8,根据 Chrome 菜单,实际编码看起来是相同的。

当我上传文件名中带有“长连字符”(“–”)的文件时,它会被转换为垃圾字符(“–”)。

垃圾字符保存在MySQL中,服务器上文件的文件名也有垃圾字符。但是,当我从数据库中提取文件名并使用 PHP 显示时,它会显示正确的连字符。

有没有办法将文件名存储为 UTF-8?当我尝试这段代码时,我得到一个错误:

$fn = iconv("CP-1252", "UTF-8", $file['name']);
debug($fn);

Notice (8): iconv(): Wrong charset, conversion from `CP-1252' to `UTF-8' is not allowed

--

几个月后更新! 所以这个问题与Windows上的一个PHP bug有关:http://bugs.php.net/bug.php?id=47096

Unicode 字符在 move_upload_file 上被 PHP 破坏 - 尽管我也看到了 rename 和 ZipArchive 的问题,所以我认为这是 PHP 和 Windows 的普遍问题。

我已经从 Wordpress 中找到了 here 的解决方法。我必须使用损坏的文件名存储文件,然后在下载/电子邮件/显示上对其进行清理。

以下是我正在使用的改编方法,以防将来对某人有用。如果您在下载/发送电子邮件之前尝试压缩文件,或者您需要将文件写入网络共享,这仍然没有多大用处。

public static function sanitizeFilename($filename, $utf8 = true)
{
if ( self::seems_utf8($filename) == $utf8 )
    return $filename;

// On Windows platforms, PHP will mangle non-ASCII characters, see http://bugs.php.net/bug.php?id=47096
if ( 'WIN' == substr( PHP_OS, 0, 3 ) ) {
        if(setlocale( LC_CTYPE, 0 )=='C'){ // Locale has not been set and the default is being used, according to answer by Colin Morelli at http://stackoverflow.com/questions/13788415/how-to-retrieve-the-current-windows-codepage-in-php
                // thus, we force the locale to be explicitly set to the default system locale
                $codepage = 'Windows-' . trim( strstr( setlocale( LC_CTYPE, '' ), '.' ), '.' );
        }
        else {
                $codepage = 'Windows-' . trim( strstr( setlocale( LC_CTYPE, 0 ), '.' ), '.' );
        }
        $charset = 'UTF-8';
        if ( function_exists( 'iconv' ) ) {

                if ( false == $utf8 ){
                    $filename = iconv( $charset, $codepage . '//IGNORE', $filename );
                }
                else {
                    $filename = iconv( $codepage, $charset, $filename );
                }
        } elseif ( function_exists( 'mb_convert_encoding' ) ) {
                if ( false == $utf8 )
                        $filename = mb_convert_encoding( $filename, $codepage, $charset );
                else
                        $filename = mb_convert_encoding( $filename, $charset, $codepage );
        }
}

return $filename;       

}

public static function seems_utf8($str) {
    $length = strlen($str);
    for ($i=0; $i < $length; $i++) {
            $c = ord($str[$i]);
            if ($c < 0x80) $n = 0; # 0bbbbbbb
            elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
            elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
            elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
            elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
            elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
            else return false; # Does not match any model
            for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
                    if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
                            return false;
            }
    }
    return true;

}

【问题讨论】:

    标签: php cakephp encoding iis-7 utf-8


    【解决方案1】:

    您说您的页面设置为 UTF8,这意味着您的 iconv() 参数是向后的。语法是

    iconv($original_char_set, $new_charset_to_convert_to, $string_to_convert);
    

    您正在上传一个 UTF-8 文件名,但随后告诉 PHP 将该字符串转换为 utf-8,就好像它是 cp1252。由于 cp-1252 是单字节字符集,所有高阶 utf-8 转义序列都被丢弃。

    【讨论】:

    • 我明白你在说什么,但即使我上传的是 UTF-8 文件名,它似乎在 POST 的另一端被视为其他内容。我尝试反转它们并得到同样的错误。我尝试将 CP-1252 更改为 ISO-8859-1,它说非法字符。我将它们反转回来,它不会抛出任何错误,但它并没有摆脱垃圾(实际上它只是以“—的形式添加了更多内容。最新功能是:iconv(“ ISO-8859-1", "UTF-8", $file['name']); 交换了 ISO 和 UTF,它说非法字符,大概是因为它正在寻找 UTF-8 中的垃圾字符而它没有存在吗?
    • 如果客户端页面设置为utf,则数据(包括文件名)应以utf形式发送。你根本不需要转换任何东西。只需确保 EVERYTHING 设置为 utf8:client->server->mysql->table->field.
    • 我应该补充一点,理想情况下,我想要一种不涉及 iconv() 的方法来解决这个问题(如果存在的话!),因为它会涉及更改我们应用程序中的每个文件上传功能。
    • 如何验证原始页面是否为 UTF-8?在 Chrome 中,我转到工具、编码和启用“自动检测”并选择 UTF-8。
    • &lt;meta http-equiv="Content-type" content="text/html; charset=utf-8&gt; 之类的。检查页面标题(包括http头),然后检查响应的标题。通常,客户端应该以与页面到达时相同的格式发回内容。
    【解决方案2】:

    更新 事实上,这是 Windows 上的一个 PHP 错误。有如下解决方法,但我见过的最佳解决方案是使用WFIO extension。此扩展为文件流提供了一个新协议wfio://,并允许 PHP 在 Windows 文件系统上正确处理 UTF-8 字符。 wfio://支持fopen、scandir、mkdir、copy、rename等多种PHP函数。

    原始解决方案

    所以这个问题与 Windows 上的一个 PHP 错误有关:http://bugs.php.net/bug.php?id=47096

    Unicode 字符在 move_upload_file 上被 PHP 破坏 - 尽管我也看到了 rename 和 ZipArchive 的问题,所以我认为这是 PHP 和 Windows 的普遍问题。

    我已经从 Wordpress 中找到了 here 的解决方法。我必须使用损坏的文件名存储文件,然后在下载/电子邮件/显示上对其进行清理。

    以下是我正在使用的改编方法,以防将来对某人有用。如果您在下载/发送电子邮件之前尝试压缩文件,或者您需要将文件写入网络共享,这仍然没有多大用处。

    public static function sanitizeFilename($filename, $utf8 = true)
    {
    if ( self::seems_utf8($filename) == $utf8 )
        return $filename;
    
    // On Windows platforms, PHP will mangle non-ASCII characters, see http://bugs.php.net/bug.php?id=47096
    if ( 'WIN' == substr( PHP_OS, 0, 3 ) ) {
            if(setlocale( LC_CTYPE, 0 )=='C'){ // Locale has not been set and the default is being used, according to answer by Colin Morelli at http://stackoverflow.com/questions/13788415/how-to-retrieve-the-current-windows-codepage-in-php
                    // thus, we force the locale to be explicitly set to the default system locale
                    $codepage = 'Windows-' . trim( strstr( setlocale( LC_CTYPE, '' ), '.' ), '.' );
            }
            else {
                    $codepage = 'Windows-' . trim( strstr( setlocale( LC_CTYPE, 0 ), '.' ), '.' );
            }
            $charset = 'UTF-8';
            if ( function_exists( 'iconv' ) ) {
    
                    if ( false == $utf8 ){
                        $filename = iconv( $charset, $codepage . '//IGNORE', $filename );
                    }
                    else {
                        $filename = iconv( $codepage, $charset, $filename );
                    }
            } elseif ( function_exists( 'mb_convert_encoding' ) ) {
                    if ( false == $utf8 )
                            $filename = mb_convert_encoding( $filename, $codepage, $charset );
                    else
                            $filename = mb_convert_encoding( $filename, $charset, $codepage );
            }
    }
    
    return $filename;       
    
    }
    
    public static function seems_utf8($str) {
        $length = strlen($str);
        for ($i=0; $i < $length; $i++) {
                $c = ord($str[$i]);
                if ($c < 0x80) $n = 0; # 0bbbbbbb
                elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
                elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
                elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
                elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
                elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
                else return false; # Does not match any model
                for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
                        if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
                                return false;
                }
        }
        return true;
    
    }
    

    【讨论】:

      【解决方案3】:

      根据https://bugs.php.net/bug.php?id=47096

      [2017-04-11 15:59 UTC] ab@php.net 已在 7.1 中修复,请参阅升级。

      谢谢。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-09-21
        • 2013-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-05
        • 1970-01-01
        相关资源
        最近更新 更多