返回损坏数据的PHP readfile()

返回损坏数据的PHP readfile(),php,cakephp,header,readfile,Php,Cakephp,Header,Readfile,在我的函数中,我保存了从base64字符串解码的图像: function saveImage(){ //magic... define('UPLOAD_DIR', '../webroot/img/'); $base64string = str_replace('data:image/png;base64,', '', $base64string); $base64string = str_replace(' ', '+', $base64string);

在我的函数中,我保存了从base64字符串解码的图像:

function saveImage(){
    //magic...
    define('UPLOAD_DIR', '../webroot/img/');
    $base64string = str_replace('data:image/png;base64,', '', $base64string);
    $base64string = str_replace(' ', '+', $base64string);
    $data = base64_decode($base64string);
    $id = uniqid();
    $file = UPLOAD_DIR.$id.'.png';
    $success = file_put_contents($file, $data);
}
上述功能正常工作,图像保存在指定文件夹中,且未损坏

在下一个函数中,我现在尝试将图像强制下载给用户:

function getChart($uniqid= null){
    if($uniqid){
        $this->layout = null;
        header("Content-type: image/png");
        header("Content-Disposition:attachment;filename='".$uniqid.".png'");
        readfile('../webroot/img/'.$uniqid.'.png');
        exit;
    } else exit;
}
从服务器下载的图像已损坏,无法显示。在文本编辑器中打开下载的文件后,我注意到在最顶端添加了一个新行字符。删除字符并保存文件后,它将正确打开并正确显示

如何修复此问题?

编辑(强制下载):

尝试以下操作(仅用于渲染图像):

此标题告诉浏览器文件有多大。某些浏览器需要它才能正确下载文件。不管怎么说,这是一个很好的方式告诉有多大的文件。这样,任何下载该文件的人都可以预测下载需要多长时间

header("Content-type: $file_type")
此标头告诉浏览器它尝试下载的文件类型

header("Content-Disposition: attachment; filename=$file_name");
这将告知浏览器以指定的名称保存此下载的文件。如果不发送此标题,浏览器将尝试使用脚本名称保存文件


但您需要使用
flush()刷新缓冲区或在您的情况下使用
ob_flush()第一个出口正上方

在调用
readfile
之前,检查您是否正在输出内容:

// add ob_start() at the very top of your script
function getChart($uniqid= null){
    echo strlen(ob_get_clean()); die(); // if it's not 0 then you are definetly echoing something

    if($uniqid){
        $this->layout = null;
        header("Content-type: image/png");
        header("Content-Disposition:attachment;filename='".$uniqid.".png'");
        readfile('../webroot/img/'.$uniqid.'.png');
        exit;
    } else exit;
}

您所描述的内容可能有多个问题,这些问题在您实际打开下载的文件之前是隐藏的

相反,要使您的代码更健壮,并检查前提条件,如果已经发送了头,则在此处检查,并清除任何可能的现有输出缓冲区,如果不可能,则给出错误:

function getChart ($uniqid = null) {

    if (!$uniqid) exit;

    $this->layout = null;

    if (headers_sent()) throw new Exception('Headers sent.');
    while (ob_get_level() && ob_end_clean());
    if (ob_get_level()) throw new Exception('Buffering is still active.');

    header("Content-type: image/png");
    header("Content-Disposition:attachment;filename='".$uniqid.".png'");
    readfile('../webroot/img/'.$uniqid.'.png');

    exit;
}

这个问题我已经有好几次了。以下是我使用的解决方案:

几乎总是我忘记关闭
download.php
文件中的
unicode BOM
编码,这会在下载文件的前面添加一些内容,从而破坏文件


因此,解决方案是在
download.php中关闭
unicode BOM

只需使用ob_clean();在readfile()之前

在调用
readline
之前,是否确定没有输出某些内容?代码看起来很好me@Peter:您正在输出某些内容。以防万一,更可能的情况是,你有一个问题,以了解在哪里。当你写的时候,输出的不仅仅是标题和readfile内容。是的,我刚刚检查了缓冲区,输出了一些东西。我不知道为什么。在你的例子中,我怎样才能强制下载图像?@DusanRadojevic:是的,但这不是问题所在。实际上,与原始代码相比,结果是一样的。在OP的环境中可能会出现完全相同的问题。缓冲区中有输出,可能是windows系统的CRLF序列,但这只是猜测。看我的答案,它会帮你清理@Peter检查打开/关闭php标记的位置。也许在标记之前有一个换行符幸运的是,使用输出缓冲的显著优势在于它与数据压缩一起使用。如果您不使用ob_gzhandler,那么就没有什么好处。flush单独提交服务器上仍然存在的任何输出数据。使用ob_start及其对应的ob_flush、ob_end_clean和ob_end_flush,任何等待压缩的内容(将flush和ob_flush视为不同的存储桶-flush将数据发送到ob,ob将数据发送到浏览器-可能不准确,但这正是想法)都将被打包并发送到客户端。
header("Content-Disposition: attachment; filename=$file_name");
// add ob_start() at the very top of your script
function getChart($uniqid= null){
    echo strlen(ob_get_clean()); die(); // if it's not 0 then you are definetly echoing something

    if($uniqid){
        $this->layout = null;
        header("Content-type: image/png");
        header("Content-Disposition:attachment;filename='".$uniqid.".png'");
        readfile('../webroot/img/'.$uniqid.'.png');
        exit;
    } else exit;
}
function getChart ($uniqid = null) {

    if (!$uniqid) exit;

    $this->layout = null;

    if (headers_sent()) throw new Exception('Headers sent.');
    while (ob_get_level() && ob_end_clean());
    if (ob_get_level()) throw new Exception('Buffering is still active.');

    header("Content-type: image/png");
    header("Content-Disposition:attachment;filename='".$uniqid.".png'");
    readfile('../webroot/img/'.$uniqid.'.png');

    exit;
}