使用Kartik Export控件导出Excel格式的文件,但是导出的文件打开时弹出如下信息:
Kartik Export —— 导出的Excel文件打开失败
错误信息是:Excel无法打开文件“xxx.xlsx",因为文件格式或文件扩展名无效。请确定文件未损坏,并且文件扩展名与文件的格式匹配。

在网上找了一些资料,说是两个可能:
1、 返回的HTTP头要区分2003和2007的格式;
2、 返回的数据有utf-8的BOM头;

我一开始是对这两个原因持怀疑态度的,因为我有2个同样的服务器,都是Centos 7 + nginx,对于同一个导出文件的链接,一个服务器是好的,另一个就有问题,而这两台服务器上的代码都是一样的,所以,如果是上面的原因的话,那应该是两台服务器都有问题才对。

通过跟踪代码,发现Kartik Export控件是先把文件保存到本地,然后再用流的方式通过浏览器输出,代码如下:
Kartik Export —— 导出的Excel文件打开失败
于是稍微修改了一下this->cleanup()里的代码,去掉了删除文件的部分:
Kartik Export —— 导出的Excel文件打开失败
执行完导出操作后,到服务器上的backend/runtime/export目录下,找到生成的Excel文件,从服务器拉下来打开,发现没问题,那就确定控件导出的文件本身是没问题的,那就看看通过浏览器下载到本地的文件:
Kartik Export —— 导出的Excel文件打开失败
发现文件的前面居然有3个字节的utf-8的BOM头,这下可以确定,直接原因就是因为BOM头导致文件打开失败。

这个BOM头到底是在什么时候被输出的呢,很遗憾,从save()到readfile()之间,写了若干调试好代码,查看缓冲区输出,都没有找到。

那问题怎么解决呢,增加个清除缓冲区的代码,问题解决:

$writer->save($file);
if ($this->stream) {
    $this->clearOutputBuffers();
    $this->setHttpHeaders();
    $this->clearOutputBuffers();
    readfile($file);
    $this->cleanup($file, $config);
    exit();
} else {

注:尝试过只保留上面第一个clearOutputBuffers(),发现不行,必须在setHttpHeaders()前后都加上才可以。

这个问题虽然最后得到了解决,其实还是留有几个尾巴,希望有知道的高手不吝指教:
1、没有搞清楚那个utf-8的BOM头到底是在哪里输出的;
2、两台服务器配置一样,为什么同样的代码,一台就会有问题,另一台就没问题;