Символы 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 

Обновите несколько месяцев спустя! Поэтому эта проблема связана с ошибкой PHP в Windows: http://bugs.php.net/bug.php?id=47096

Unicode символы обрабатываются PHP на move_upload_file – хотя я также видел проблему с rename и ZipArchive, поэтому я думаю, что это общая проблема с PHP и Windows.

Я адаптировал обходное решение из WordPress, найденного здесь . Я должен хранить файл с измененным именем файла, а затем дезинфицировать его при загрузке / электронной почте / показе.

Вот адаптированные методы, которые я использую, если они будут полезны кому-то в будущем. Это все еще не очень полезно, если вы пытаетесь заархивировать файлы перед загрузкой / отправкой по электронной почте или вам необходимо записать файлы в общий сетевой ресурс.

 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; } 

Вы говорите, что ваша страница настроена для UTF8, что означает, что ваши аргументы iconv () возвращаются назад. Синтаксис

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

Вы загружаете имя файла UTF-8, но затем говорите PHP, чтобы преобразовать эту строку в utf-8, как если бы это был cp1252. Так как cp-1252 является однобайтовой кодировкой, все escape-последовательности utf-8 высокого порядка получаются разбитыми.

UPDATE Действительно, это ошибка PHP в Windows. Есть обходные пути, как показано ниже, но лучшим решением, которое я видел, является использование расширения WFIO . Это расширение предоставляет новый протокол wfio:// для потоков файлов и позволяет PHP правильно обрабатывать символы UTF-8 в файловой системе Windows. wfio:// поддерживает ряд функций PHP, включая fopen, scandir, mkdir, copy, rename и т. д.

оригинальное решение

Поэтому эта проблема связана с ошибкой PHP в Windows: http://bugs.php.net/bug.php?id=47096

Unicode символы обрабатываются PHP на move_upload_file – хотя я также видел проблему с rename и ZipArchive, поэтому я думаю, что это общая проблема с PHP и Windows.

Я адаптировал обходное решение из WordPress, найденного здесь . Я должен хранить файл с измененным именем файла, а затем дезинфицировать его при загрузке / электронной почте / показе.

Вот адаптированные методы, которые я использую, если они будут полезны кому-то в будущем. Это все еще не очень полезно, если вы пытаетесь заархивировать файлы перед загрузкой / отправкой по электронной почте или вам необходимо записать файлы в общий сетевой ресурс.

 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; } 

Согласно https://bugs.php.net/bug.php?id=47096

[2017-04-11 15:59 UTC] ab@php.net Исправлено в 7.1, см. ОБНОВЛЕНИЕ.

Благодарю.