上传文件名中的UTF-8字符在文件上传时混乱

时间:2013-12-05 19:02:02

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

我在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错误有关:http://bugs.php.net/bug.php?id=47096

在move_upload_file上,PHP字符被PHP破坏了 - 虽然我也看到了重命名和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 个答案:

答案 0 :(得分:0)

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

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

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

答案 1 :(得分:0)

<强>更新 实际上这是Windows上的PHP错误。有类似下面的解决方法,但我看到的最佳解决方案是使用WFIO extension。此扩展为文件流提供了新协议wfio://,并允许PHP在Windows文件系统上正确处理UTF-8字符。 wfio://支持许多PHP函数,包括fopen,scandir,mkdir,copy,rename等。

原始解决方案

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

在move_upload_file上,PHP字符被PHP破坏了 - 虽然我也看到了重命名和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;

}

答案 2 :(得分:0)

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

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

感谢。