人生在于折腾:php实现下载导出xx.tar.gz
刚接到这样的需求,其实我是拒绝的。我甚至很有耐心地和pm商量,扔个csv不就好了么?
pm:对方需要一个csv打包成.tar.gz的包,他们是linux server,这是硬性要求。
然后我开始折腾之旅,里面小坑无数。
其实这里大致有两条思路:
1.把生成好的csv利用System(),exec()函数去使用系统命令tar成包,比较蛋疼的是公司配的是windows,我只能在自己虚拟机ubuntu里面测试。
2.利用现成的工具类或者函数什么的, 去直接生成一个tar.gz,然后扔csv进去。
我特么先选第二条路。首先别人是要.tar.gz,我先去查了一下php的内置函数,貌似只有gzwrite,gzopen等文件操作函数,只能把文件弄成安全二进制的.gz文件。我发现如果继续走下去就走到第1条路上了,只能利用系统命令去实现。
我尝试性地在ubuntu下exec('tar -cxf do.tar.gz do.csv'),然而只有run script方式可以执行生成成功,而通过浏览器并没有生成。没得到tar.gz,何谈后面利用一连串header去输出下载文件呢?
所以第一条路是彻底死了,我继续在第二条路上行走。
由于网络不好,没能连上vpn。我只能一直百度,而不能利用到google优质的资源。我搜索了相关php导出tar.gz的资料,国内一大堆抄袭的,基本结论是:少数人遇到这个需求,且遇到了下载出来的文件破损不能打开,无解。
你没看错,是:无解。国内没有一个人解决了这个问题(至少在能搜索出来的page上来看)。我先试着曲线救国,先还是用gzwrite系列函数去创建一个.gz文件,代码如下:
<?php $gz = gzopen('/tmp/do.gz', 'w9'); // 打开或创建一个.gz文件,linux下记得写权限问题
gzwrite($gz, '33c,24ec,32q3');// 写入内容到.gz文件
gzclose($gz); // 关闭文件句柄,释放资源
header("Content-Type: application/x-gzip");
$lastPackName = '/tmp/do.gz';
//header("Content-type:application/octet-stream");
header("Accept-Ranges:bytes");
header("Accept-Length: ".filesize($lastPackName));
Header("Content-Disposition:attachment; filename=records.gz");
@readfile($lastPackName);
//下载文件之后,要删除该压缩包
@unlink($lastPackName);
相关函数的用法,可以自行查阅php.net上的相关函数。然后我发现单独写一个demo文件,是能下载下来能正常解压的文件,而如果写到公司项目中对应的Controller的function里面,则下载的包是破损的,这是我遇到的第一个坑。
然后我又在想,是否是环境问题。我把下载好的文件包,扔到我虚拟机(ubuntu)里面,也是解压出来有问题。而在服务器上生成的原始文件.gz是完好无缺的。所以我遇到了和这位同学一样的问题:http://bbs.csdn.net/topics/390226194
最后他的提问被说是header头设置的问题,我又仔细排查了一下header设置没有问题。
后面我知道pear有个Archive_Tar类可以直接打包tar文件,我分析了一下,需要安装pear相应的包,成本较大,放弃了这样的做法。
昨天整个下午就在不断下载解压失败和查看国内千篇一律无解的技术资料上消耗完了。晚上回到家,我不甘心,连上vpn,怒用google,很快查到了这篇文章:
http://stackoverflow.com/questions/7004989/creating-zip-or-tar-gz-archive-without-exec
歪果仁果然会折腾,早就遇到类似打包的要求,并且没有使用exec.可以使用php自带的PharData类去创建tar文件,然后通过compress函数打包成tar.gz.关于PharData类和相关函数可以去php.net上查阅。
于是我使用如下代码打包:
<?php $csvFile = '/tmp/do.csv';
$fp = fopen($csvFile , 'w+');
fwrite($fp, '232,c233,23dc');
fclose($fp);
$tarFile = '/tmp/archive.tar';
$a = new PharData($tarFile); // ADD FILES TO archive.tar FILE
$a->addFile($csvFile, 'do.csv');
$a->compress(Phar::GZ);
unlink($tarFile);
unlink($csvFile);
header("Content-Type: application/x-gzip");
$lastPackName = $tarFile . '.gz';
//header("Content-type:application/octet-stream");
header("Accept-Ranges:bytes");
header("Accept-Length: ".filesize($lastPackName));
Header("Content-Disposition:attachment; filename=records.tar.gz");
@readfile($lastPackName);
//下载文件之后,要删除该压缩包
@unlink($lastPackName);
我果真so easy地创建了一个包含xx.csv的tar.gz文件,但是下载解压失败依然存在。当然,如果写一个小脚本直接用浏览器访问是对的,下载的文件也是解压正常。
时间不知不觉就到了今天上午,和部门leader一起排查,先排除了ngnix转发输出的问题(在测试服务器上测试某段代码),也排除了生成文件部分的问题。代码就那么十几行,最后只剩下header输出到最后readfile这部分。
readfile是把文件读到缓冲区里面,而下载下来有问题,多半是buffer区在readfile之前有东西了。于是我bing了一下"php header gz broken"关键字,查到这篇讨论:
http://stackoverflow.com/questions/22046020/php-downloading-tar-gz-file-increases-file-size-and-changes-md5
虽然并不是完全和我想要的内容一样,不过给了我一点提示。我发现他在readfile之前执行了ob_clean(),清除缓冲区。这个函数保证了后面的输出都是干净的,新的。最终我的代码如下:
$csvFile = '/tmp/downPrivilege.csv';
$fp = fopen($csvFile, 'w+') or die('cant not create file');
fwrite($fp, '23233,cw3,232');
fclose($fp);
$tarFile = '/tmp/downPrivilege.tar';
$a = new PharData($tarFile); // ADD FILES TO archive.tar FILE
$a->addFile($csvFile, 'downPrivilege.csv');
$a->compress(Phar::GZ);
header("Content-Type: application/x-gzip");
$lastPackName = $tarFile . '.gz';
header("Accept-Ranges:bytes");
header("Accept-Length: " . filesize($lastPackName));
Header("Content-Disposition:attachment; filename=downPrivilege.tar.gz");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private");
header("Pragma: public");
ob_clean(); // 清除缓冲区内容
@readfile($lastPackName);
unlink($tarFile);
unlink($csvFile);
unlink($lastPackName);
下载之后终于正确能解压出东西了。
总结:
1.不要想着别人能帮助你 ,特别是比较不常见的东西,只能自己专研。我以前觉得有团队可以一起探讨,但是实际工作中很少人愿意花时间真的和你讨论。
2.查资料真心不要百度,常见的问题都可能是错误解答,而且千篇一律。还是bing+google吧。
3.排查问题要自信,过滤掉的环节就不要反复折腾。
4.歪果仁往往已经遇到了那些非主流的情况,可以直接借鉴。
希望有做类似需求的朋友可以看到这篇文章,因为国内没有一个人能正面回答这个问题。
人生在于折腾:php实现下载导出xx.tar.gz的更多相关文章
- Android SDK 5.0 这个语句带来折腾 - 生命在于折腾!
Android SDK 5.0 带来的这番折腾 - 生命在于折腾! 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一 ...
- 下载jdk文件后缀是.gz而不是.tar.gz怎么办
用chrom浏览器下载了linux版的jdk,发现文件后缀是.gz,没看过这玩意,一打开,还是一个.gz文件,原本以为是新文件后缀呢.那个百度google啊. . ..最后都没发现有这方面的资料啊.. ...
- mysql-cacti-templates-1.1.2.tar.gz 免费下载 cacti MySQL添加监控
cacti MySQL添加监控 1. 安装监控插件 wget http://mysql-cacti-templates.googlecode.com/files/mysql-cacti-templat ...
- mysql-5.6.45-linux-glibc2.12-x86_64.tar.gz下载安装
一 ,mysql下载 需要注册,可以通过组合url越过注册下载需要的包. 下载地址: https://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.3 ...
- [转]protobuf-2.5.0.tar.gz的下载与安装
protobuf-2.5.0.tar.gz的下载与安装 原文地址:http://blog.csdn.net/tdmyl/article/details/31811317 版权声明:本文为博主原创文章, ...
- Linux下编译Qt源码,一定要下载tar.gz版本,否则会报权限不足
首先下载qt-everywhere-opensource-src-4.8.1源码,下载地址: ftp://ftp.qt-project.org/qt/source/ 在Linux下编译一定要下载qt- ...
- protobuf-2.5.0.tar.gz的下载与安装
1.下载 hadoop使用protocol buffer进行通信,须要下载和安装protobuf-2.5.0.tar.gz.因为如今protobuf-2.5.0.tar.gz已经无法在官网https: ...
- Elasticsearch-6.7.0系列(一)9200端口 .tar.gz版本centos7环境--下载安装运行
https://www.elastic.co/guide/index.html(推荐) ES官方英文原版文档,一般会更新到最新版本 https://www.elastic.co/cn/d ...
- wget http://pypi.python.org/packages/source/s/setuptools/setuptools-2.0.tar.gz 下载时报错 ssl is required 解决办法
方法一:使用浏览器下载.在浏览器中输入 http://pypi.python.org/packages/source/s/setuptools/setuptools-2.0.tar.gz 方法二:将h ...
随机推荐
- SALT 加密
大家都知道,MD5加密是不可逆.但事实上,我们通常值的MD5算法.黑客的眼下破解率相对较高.也有非常多站点上干脆就提供批量解密MD5的服务,当然是收费的.http://www.xmd5.org.这里提 ...
- memcpy源代码
7月22号去面试开发的职位,面试官先问我在以前项目中写了什么程序.我就巴拉巴拉的说了一堆写过的code,主要还是测试工具和自动化测试代码.之后让我写memcpy的函数,面试官还挺好的,帮我把函数原型都 ...
- SSIS如何引用外部DLL
原文:SSIS如何引用外部DLL 当SSIS引用外部的DLL时,外部的DLL须满足以下条件: 1. DLL是强命名. 2. 加入到GAC (C:\WINDOWS\assembly),直接把DLL拉进目 ...
- Android 5.0自定义动画
材料设计中的动画对用户的操作给予了反馈,并且在与应用交互时提供了持续的可见性.材料主题提供了一些按钮动画和活动过渡,Android 5.0允许你自定义动画并且可以创建新的动画: Touch Feedb ...
- WXPP QuickFramework V2.0
微信快速开发框架(WXPP QuickFramework)V2.0版本上线--源码已更新至github 用了一个多星期的时间,把微信快速开发框架进行了改进,之前1.0版本针对的是普通订阅号,V2. ...
- 利用Matlab生成一个网格化的三维三轴椭球面(生成直角坐标)
代码很简单,a,b,c分别为椭球的三轴轴长,a=b=c时得到的是三维球面,a=b!=c时得到的是三维椭球面,a!=b且a!=c且b!=c时得到的是三维旋转椭球面 %生成一个笛卡尔坐标系下三轴椭球表面的 ...
- C# 学习笔记1 .NET平台,C#的重要概念
.NET平台构成的三个关键实体是: 1.CLR(公共语言运行库):为我们定位,加载,管理.NET类型,同时负责一些底层细节的工作,如内存管理,应用托管,处理线程,安全检查等,它包含了一个重要名为msc ...
- 1 MySQL概述
目录: 1. 简述 2. 历史 3. 同类产品 4. 优点和不足 5. MySQL存储引擎 6. MySQL架构 1. 简述 MySQL是一个关系型数据库管理系统.其体积小,速度快,开发源代码,使用成 ...
- Xcode中如何集成Unity
项目中需要集成unity,摸索了大半周,碰到了很多坑,终于搞定. 我的方法是,通过unity导出一个空的iOS项目,然后再新建一个Xcode项目,针对配置页面一一对应.直到配置完全一样,然后倒入相关资 ...
- @Html.CheckBoxFor为何输出两种控件
在MVC中当使用@Html.CheckBoxFor时表单上会产生两种控件checkbox和hidden,比如: @Html.CheckBoxFor(model => model.IsTop) 对 ...