开发中大家都是使用的utf8编码,昨天遇到一个奇坑,本是一件很小的问题,解决也浪费了个吧小时。废话不多说,植入正题:
文件下载方式:通过header二进制流文件下载
需求: 文件上传保留文件名不变
数据字段file_url值:/public/upload/files/2019/04-29/中文测试包.rar
linux(Ubuntu 18.04.2 LTS )文件目录:/home/wwwroot/web/public/upload/files/2019/04-29
windows10文件目录:D:\web\public\upload\files\2019\04-29\中文测试包.rar
我们先看下,windows下的文件下载:
<?php $file_name = '/public/upload/files/2019/04-29/中文测试包.rar'; //$file_name = iconv("utf-8","gbk//IGNORE",$file_name); // 特别注意!特别注意!特别注意这里,windows下必须开转码,不然直接文件不存 $file_path = $_SERVER['DOCUMENT_ROOT'] . $file_name;// 比如windows下这里我的是 "D:/web/public/upload/files/2019/04-29/中文测试包.rar" //判断如果文件存在,则跳转到下载路径 if (!file_exists($file_path)) { die("文件不存在!"); } $fp = fopen($file_path, "r+") or die('打开文件错误'); //下载文件必须要将文件先打开。写入内存 $file_size = filesize($file_path); //返回的文件流 Header("Content-type:application/octet-stream"); //按照字节格式返回 Header("Accept-Ranges:bytes"); //返回文件大小 Header("Accept-Length:" . $file_size); //弹出客户端对话框,对应的文件名 Header("Content-Disposition:attachment;filename=" . substr($file_name, strrpos($file_name, '/') + 1)); //防止服务器瞬间压力增大,分段读取 $buffer = 1024; while (!feof($fp)) { $file_data = fread($fp, $buffer); echo $file_data; } fclose($fp); die("下载成功!"); ?>
文件不存在?神马玩意?。同样的代码ubutun生产环境下:
文件下载成功。神马情况?
原因:windows 系统默认字符集是gbk,项目采用的是uft8编码,中文文件名必须转码才能使用file_exists检测文件,不然报找不到文件:
windows下的解决方式就是上面注释的那一段开启:
$file_name = iconv("utf-8","gbk//IGNORE",$file_name); // 特别注意!特别注意!特别注意这里,windows下必须开转码,不然直接文件不存
windows下再次执行后发现下载成功:
那么问题来了。开启后的代码是这样的:
<?php $file_name = '/public/upload/files/2019/04-29/中文测试包.rar'; $file_name = iconv("utf-8","gbk//IGNORE",$file_name); // 特别注意!特别注意!特别注意这里,windows下必须开转码,不然直接文件不存 $file_path = $_SERVER['DOCUMENT_ROOT'] . $file_name;// 比如windows下这里我的是 "D:/web/public/upload/files/2019/04-29/中文测试包.rar" //判断如果文件存在,则跳转到下载路径 if (!file_exists($file_path)) { die("文件不存在!"); } $fp = fopen($file_path, "r+") or die('打开文件错误'); //下载文件必须要将文件先打开。写入内存 $file_size = filesize($file_path); //返回的文件流 Header("Content-type:application/octet-stream"); //按照字节格式返回 Header("Accept-Ranges:bytes"); //返回文件大小 Header("Accept-Length:" . $file_size); //弹出客户端对话框,对应的文件名 Header("Content-Disposition:attachment;filename=" . substr($file_name, strrpos($file_name, '/') + 1)); //防止服务器瞬间压力增大,分段读取 $buffer = 1024; while (!feof($fp)) { $file_data = fread($fp, $buffer); echo $file_data; } fclose($fp); die("下载成功!"); ?>
在ubutun 服务器上我们执行:
是不是仿佛解决东墙补西墙。ubutun 下字符集可以通过:
cat /usr/share/i18n/SUPPORTED
说明系统支持中文字符,不然上传的压缩包怎么会显示:“中文测试包.rar”。
问题描述:linux系统下验证中文文件file_exists不能是中文,所以不能在上面转码成gbk.
那么问题来了: 如何做到兼容性?
我们知道PHP_OS是 php自带的一个内置常量,返回的是服务器端的操作系统标示,值为(WINNT,WIN32等),比如这样:
echo strtoupper(substr(PHP_OS,0,3))==='WIN'?'windows 服务器':'不是 widnows 服务器';
另外一种通过系统分隔符DIRECTORY_SEPARATOR ,这个也是php自带的一个内置常量,用来显示系统分隔符的命令,
不需要任何定义与包含即可直接使用。在windows下路径分隔符是\(当然/在部分系统上也是可以正常运行的),在linux上路径的分隔符是/,