codeigniter 2 中文乱码需http层(强制响应头charset=utf-8)、文件层(gbk转utf-8处理$_files中文名)、数据库层(全程utf8mb4)三路协同,缺一不可。

CodeIgniter 2 跨平台中文乱码不能靠单点修补,必须从 HTTP 层、文件层、数据库层三路齐下——否则改了 URL 参数乱码,文件上传又崩;修好了上传,数据库读出来又是问号。
HTTP 响应与 HTML 渲染必须强制 UTF-8
CI2 默认不设 charset,浏览器可能按 GBK 解析,导致页面中文显示为方块或“测试”。关键不是加 meta 标签,而是让响应头真实生效:
-
header('Content-Type: text/html; charset=utf-8')必须在任何输出前调用(包括空白符),CI2 中建议写在application/core/MY_Controller.php构造函数开头 -
<meta charset="utf-8">是 fallback,不能替代响应头;若用 CI 的$this->output->set_content_type('text/html; charset=utf-8'),要确保没被后续echo或错误触发提前输出 - 检查实际响应头:用浏览器开发者工具 > Network > Headers > Response Headers,确认
Content-Type含charset=utf-8,而非charset=gbk或缺失
$_FILES 中文文件名在 Windows+IE/Edge 下默认是 GBK 编码
这是 CI2 文件上传乱码最隐蔽的根源:浏览器把“测试.pdf”以 GBK 字节发过来,CI2 拿 $_FILES['userfile']['name'] 当 UTF-8 处理,结果 file_exists() 查不到、move_uploaded_file() 写失败、$this->upload->data('file_name') 返回乱码字符串。
- 不要只改
set_filename()—— 必须同步处理两处:file_exists($path . $filename)前转 GBK,move_uploaded_file()路径也转 GBK - 在
system/libraries/Upload.php的set_filename()方法里,插入:$filename = iconv('UTF-8', 'GB2312//IGNORE', $filename);(仅当!$this->encrypt_name) - 在
do_upload()中,找到@move_uploaded_file($this->file_temp, $this->upload_path . $this->file_name),改为:@move_uploaded_file($this->file_temp, iconv('UTF-8', 'GB2312//IGNORE', $this->upload_path . $this->file_name)) - Linux 服务器可跳过
iconv,但跨平台部署时保留更安全;//IGNORE很关键,避免个别字符无法转换导致整个上传中断
数据库连接与字段编码必须全程 utf8mb4
CI2 的 database.php 配置项 char_set 和 dbcollat 若只设 utf8,MySQL 5.5.3+ 会截断 emoji 和部分生僻汉字,表现为插入变问号、读出为空或乱码。
-
$db['default']['char_set'] = 'utf8mb4';(不是utf8) $db['default']['dbcollat'] = 'utf8mb4_unicode_ci';- MySQL 服务端也要配:my.cnf 中
[mysqld]下加character-set-server = utf8mb4和collation-server = utf8mb4_unicode_ci - 已有表需手动升级:
ALTER TABLE `table_name` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
URL 中文参数要用 urldecode() 显式解码
CI2 的 URI 类不自动解码路径段,http://site.com/news/view/方式-盛大富翁 中的 $slug 实际是 %E6%96%B9%E5%BC%8F-%E7%9B%9B%E5%A4%A7%E5%AF%8C%E7%BF%81,直接 echo 就是乱码。
- 控制器方法里必须手动解码:
$slug = urldecode($slug); - 不要依赖
url_title()或其他辅助函数预处理——它们不解决原始参数编码问题 - 若用路由映射(如
$route['news/(:any)'] = 'news/view/$1';),$1仍是编码后字符串,一样要urldecode()
真正麻烦的不是某一处改错,而是三者耦合:前端发 UTF-8 请求头,后端却用 GBK 处理文件名;数据库存了 utf8mb4,PHP 连接却用旧版 utf8;URL 参数解码了,模板里又用错编码输出。每层都得验一次实际字节流,而不是只看“看起来像不像”。











