开发中大家都是使用的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上路径的分隔符是/,

DIRECTORY_SEPARATOR 这个常量存在的意义就是会根据不同的操作系统来显示不同的分隔符。

使用 DIRECTORY_SEPARATOR 判断操作系统类型比如这样:

echo DIRECTORY_SEPARATOR=='\\'?'windows 服务器':'不是 widnows 服务器';

还有一种方式:

PATH_SEPARATOR 也是一个常量,在linux系统中是一个" : "号,Windows上是一个";"号。

使用 PATH_SEPARATOR 判断操作系统类型比如这样:

echo PATH_SEPARATOR==';'?'windows 服务器':'不是 widnows 服务器';

代码兼容性我们可以验证系统类型,对windows下做判断再决定是否转码操作。
这里重点说哈关于下载后文件打开提示“文件损坏”的问题,期初我也遇到。猜测肯定是在读取文件字节流存在数据丢失,也就是没读取完整:

下面看下这段有问题的代码:有兴趣的朋友可以自己思考哈,问题在哪里?这里我就不说了,相信很多朋友也能找到问题点:

<?php
$http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://'; $file_name = '/public/upload/files/2019/04-29/中文测试包.rar'; //检测文件是否存在,并且可读
if (!is_file($file_name) && is_readable($file_name)) {
die("文件不存在或不可读!");
} //判断如果文件存在,则跳转到下载路径
$file_path = $_SERVER['DOCUMENT_ROOT'] . $file_name;// 比如windows下这里我的是 "D:/web/public/upload/files/2019/04-29/中文测试包.rar"
//判断如果文件存在,则跳转到下载路径
if (!file_exists($file_path)) {
die("文件不存在!");
} //设置脚本的最大执行时间,设置为0则无时间限制
set_time_limit(0);
ini_set('max_execution_time', '0');
//通过header()发送头信息
//因为不知道文件是什么类型的,告诉浏览器输出的是字节流
header('content-type:application/octet-stream');
//告诉浏览器返回的文件大小类型是字节
header('Accept-Ranges:bytes'); //获得文件大小
//$filesize = filesize($filename);//此方法无法获取到远程文件大小 $header_array = get_headers($http_type . $_SERVER['HTTP_HOST'] . $file_name, true);
$filesize = $header_array['Content-Length'];
//告诉浏览器返回的文件大小
header('Accept-Length:' . $filesize); //告诉浏览器文件作为附件处理并且设定最终下载完成的文件名称
header('content-disposition:attachment;filename=' . substr($file_name, strrpos($file_name, '/') + 1)); //针对大文件,规定每次读取文件的字节数为4096字节,直接输出数据
$buffer = 4096;
$fp = fopen($file_path, 'rb');
//总的缓冲的字节数
$sum_buffer = 0; //只要没到文件尾,就一直读取
while (!feof($fp) && $sum_buffer < $filesize) {
echo fread($fp, $buffer);
$sum_buffer += $buffer;
} //记录下载
die("下载成功!"); ?>

有兴趣的朋友可以找下bug,哈哈

php 通过header下载中文文件名 压缩包损坏或文件不存在的问题的更多相关文章

  1. LINUNX下PHP下载中文文件名代码

            function get_basename($filename){                 return preg_replace('/^.+[\\\\\\/]/', '',  ...

  2. springMVC下载中文文件名乱码【转】

    //遇到的现象是,下载含有中文文件名的文件时,能获取到文件,但是使用IE正常,使用firefox,chrome文件名却乱码.//既然如此,就区分一下浏览器再返回好了,处理方式如下 //RESTfull ...

  3. springMVC下载中文文件名乱码【原】

    重点就在于添加  "attachment;filename*=utf-8'zh_cn'" + fileName //遇到的现象是,下载含有中文文件名的文件时,能获取到文件,但是使用 ...

  4. safari下载中文文件名乱码

    原因:响应头设置content-disposition,主要遵循 RFC 5987标准. response.setHeader("content-disposition",&quo ...

  5. java 解决safari下载中文文件名乱码

    主要就是在响应头设置content-disposition,主要遵循 RFC 5987标准. response.setHeader("content-disposition",&q ...

  6. 非Windows系统 如何解压带中文密码和中文文件名的zip压缩文件

    数据科学交流群,群号:189158789 ,欢迎各位对数据科学感兴趣的小伙伴的加入! 一.安装unar软件包: Linux(Debian系列): apt install unarLinux(RedHa ...

  7. IE浏览器下载文件中文文件名乱码问题解决

    处理过程 根据IE的F12中的log提示,是因为http头信息中的编码替换了html文件中的编码.我最初的思路是设置Tomcat默认编码,但是我发现我已经在Server.xml中设置过,想到这里我想到 ...

  8. python用ftplib上传下载中文报错解决

    python中的中文编码一直以来都是一个极为头大的问题,经常抛出编码转换的异常,python中的str和unicode到底是一个什么东西呢?在python中提到unicode,一般指的是unicode ...

  9. 上传文件,经过Zuul,中文文件名乱码解决办法

    转载请标明出处: http://blog.csdn.net/forezp/article/details/77170470 本文出自方志朋的博客 问题描述 在项目中又一个上传文件的oss服务,直接调用 ...

随机推荐

  1. CSS——LESS【转】

    原文链接:http://www.w3cplus.com/css/less 去年就初次接触了LESS,并用他制作了一个Less.org的首页页面,但由于CSS的固有模式,没有让自己喜欢上他.由于前段时间 ...

  2. Python库

    --Python库之Pandas库-------- 自主选择学习了Python中的Pandas库,以下是本人对Pandas库的认识: Pandas库是Python最受欢迎的库之一,主要用于数据的操作. ...

  3. Egg Dropping Puzzle问题的分析

    首先,基本问题是这样:You are given two eggs, and access to a 100-storey building. The aim is to find out the h ...

  4. 语音识别中的CTC算法的基本原理解释

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文作者:罗冬日 目前主流的语音识别都大致分为特征提取,声学模型,语音模型几个部分.目前结合神经网络的端到端的声学模型训练方法主要CTC和基 ...

  5. js基础进阶--编码实用技巧(二)

    我的个人博客:http://www.xiaolongwu.cn 接上篇文章 js编码的实用技巧(一) 5.合理利用||运算符 使用||可以作为参数之外的默认值,当第一个参数返回值为false时,那么第 ...

  6. 功能强大的swagger-editor的介绍与使用

    一.Swagger Editor简介 Swagger Editor是一个开源的编辑器,并且它也是一个基于Angular的成功案例.在Swagger Editor中,我们可以基于YAML等语法定义我们的 ...

  7. D. Kuro and GCD and XOR and SUM

    Kuro is currently playing an educational game about numbers. The game focuses on the greatest common ...

  8. ucloud发送短信的php sdk

    在ucloud官方的版本中,只有python的sdk可供调用,现提供php的sdk发送短信 项目地址:https://github.com/newjueqi/ucloudsms 使用方法: (1)在c ...

  9. 【深度学习系列】PaddlePaddle垃圾邮件处理实战(一)

    PaddlePaddle垃圾邮件处理实战(一) 背景介绍   在我们日常生活中,经常会受到各种垃圾邮件,譬如来自商家的广告.打折促销信息.澳门博彩邮件.理财推广信息等,一般来说邮件客户端都会设置一定的 ...

  10. Python bytes数据类型

    Python3 中文本是Unicode, 由str类型表示. 二进制数据由bytes类型表示(如视频文件). Python3 不会以任意隐式的方式 滥用str和bytes, 所以不能拼接字符串和字节包 ...