从PHP下载时Word/Excel文件已损坏

从PHP下载时Word/Excel文件已损坏,php,docx,xlsx,Php,Docx,Xlsx,我正在将一个简单的文件上传/下载功能构建到我的数据库中。唯一复杂的部分是,所有文件都需要使用我喜欢的shmancy加密方法进行加密 所以我要做的是创建一个SQL条目来存储诸如:id_文件、文件名、扩展名、大小、添加日期等内容 一旦我得到了id\u文件,我就获取文件内容,对它们进行加密,然后将内容保存到我的服务器上,作为[id\u file].txt 下面是再次下载该文件的代码: header("Pragma: public"); header('Content-Disposition: atta

我正在将一个简单的文件上传/下载功能构建到我的数据库中。唯一复杂的部分是,所有文件都需要使用我喜欢的shmancy加密方法进行加密

所以我要做的是创建一个SQL条目来存储诸如:id_文件、文件名、扩展名、大小、添加日期等内容

一旦我得到了id\u文件,我就获取文件内容,对它们进行加密,然后将内容保存到我的服务器上,作为[id\u file].txt

下面是再次下载该文件的代码:

header("Pragma: public");
header('Content-Disposition: attachment;filename="'.$file['name'].'.'.$file['extension'].'"');
header('Cache-Control: max-age=0');

echo someFunctionIMadeForGettingAndDecryptingFileContents($_GET['id_file']);

exit;
非常简单的东西,适用于除.docx和.xlsx之外的所有文件类型。下载.docx或.xlsx文件时,Office给我一个错误提示“Word在“文件名”中找到不可读的内容。是否要恢复此文档的内容?如果您信任源…bla bla”,我单击“是”。它思考了一下,文件打开得很好。但很明显,如果我的客户每次都会犯这样的错误,我就不能让他们使用这个

我编写的代码对所有其他文件类型都非常有效。即使是.doc、.xls和.zip文件也可以正常工作

我的第一个想法是看标题。我尝试过各种解决方案,如下面列出的:

那些没用

我知道问题可能是文件中添加了额外的填充或空白。但是如果我上传一个.txt文件,然后再下载一次。。。我看不出有什么额外的添加

如果我对原始文件(good.docx)和原始文件的下载版本(bad.docx)进行MD5,则哈希值是不同的

如果我将good.docx更改为good.zip并解压缩归档文件。然后对bad.docx执行同样的操作。然后MD5两个目录,散列是相同的。我在good.zip和bad.zip中散列了每个文件,每个文件散列都是相同的

还要注意的是,在我服务器的其他地方,我使用PHPWord和PHPExcel动态生成Office文件,这些文件都下载得很好。我用于PHPExcel的标题/代码是:

header("Pragma: public");
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="'.$filename.'.xlsx"');
header('Cache-Control: max-age=0');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
$objWriter->save('php://output');
exit;
(是的,我尝试在上面的其他代码中使用“Content-Type”标题,但没有帮助。)

我还尝试将文件保存在服务器上,下载并打开它。在这个过程中,我也会遇到同样的错误。下面是我用来做这件事的代码:

$f=fopen("/myPath/temp.docx","w");
fwrite($f,someFunctionIMadeForGettingAndDecryptingFileContents($_GET['id_file']));
fclose($f);
exit;
我尝试创建一个名为“blank.docx”的空Word文件。然后将其改为“保存新文件”功能。。。。它用解密的文件内容替换blank.docx的内容。但在下载blank.docx之后,我得到的都是一样的。。。一个错误,但它最终会打开。最初位于blank.docx上的文件属性(如Template:Normal.dotm)都不在已修改的blank.docx上

我正在使用Office2007

更新

以下是下载文件的良好(原始)版本的链接:

这里有一个链接,可以下载该文件的错误(已处理)版本:

解决方案

正如Llama先生在下面指出的,我的加密功能是删除一些额外的空字节。但事实证明,罪魁祸首并不像你想象的那么明显。这是我的加密:

trim(base64_encode(IV.mcrypt_encrypt(MCRYPT_RIJNDAEL_128,ENCKEY,$contents,MCRYPT_MODE_CBC,IV)))
问题不在于trim()或base64_encode()。它是用mcrypt函数实现的。我解决这个问题的方法是在将文件内容传递给加密之前,我做了另一个base64_encode()。所以像这样

$file_contents_encrypted=base64_encode(myEncryptionFunction($file_contents));
当然,解密后的情况正好相反


base64_编码技术上运行了两次。但是我可以看出,在这种情况下,在mcrypt之前需要如何运行它,因为.docx和.xlsx具有独特的类似zip的格式,您的解密函数正在删除文件末尾的空字节

good.docx
文件以四个
0x00
字节结尾,而
bad.docx
文件以无字节结尾。除了丢失的字节外,这些文件是相同的

如果跳过
good.docx
的最后四个字节,md5和将完全匹配:

$ head -c -4 good.docx | md5sum
fbd32fbcc02d62dfd8bd39d390252a4b *-

$ cat bad.docx | md5sum
fbd32fbcc02d62dfd8bd39d390252a4b *-

您是否尝试将
Content Transfer Encoding
设置为
base64
并使用
base64
算法对二进制数据进行编码。我已尝试将内容传输编码设置为二进制。我现在将尝试base64。您将如何使用base64算法对二进制数据进行编码?可能是日期的问题?如果md5校验和相等,则是另一回事。。也许每次保存docx时都会根据文件保存的日期创建散列?您说过下载后MD5总和不同。你能上传好/坏样本进行分析吗?是的。。。这听起来是可能的。我的加密和解密函数都使用trim()。我现在将尝试删除它…从encrypt和decrypt中删除trim()没有帮助。但我很确定这是一条正确的道路(并且会给你答案)。你能回答最后两个问题吗。。。1) 您要输入什么命令行来获取这些读数?2) 有没有办法手动将这四个空字节添加回bad.docx以查看它是否正确打开?我在Windows上使用Cygwin,但是命令(出现在
$
之后)在任何Unix系统上都可以正常工作。如果您都没有访问权限,您可以下载一个十六进制编辑器来手动添加缺少的字节。要使用Cygwin/Unix手动添加字节,以下操作将起作用:
head-c4/dev/zero>>bad.docx
我已经让它工作了!!!我会用解决方案更新我的问题,以防这里的任何人好奇,或者其他人发生这种情况。再次感谢骆驼先生!我有能力的时候会给你赏金的。
$ head -c -4 good.docx | md5sum
fbd32fbcc02d62dfd8bd39d390252a4b *-

$ cat bad.docx | md5sum
fbd32fbcc02d62dfd8bd39d390252a4b *-