PHP readfile()返回损坏的数据

时间:2012-09-28 11:58:17

标签: 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);
    $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;
}

从服务器下载的图像已损坏,无法显示。在文本编辑器中打开下载的文件后,我注意到在最顶部添加了一个新的行字符。删除字符并保存文件后,它会正确打开并正确显示。

我该如何解决这个问题?

6 个答案:

答案 0 :(得分:1)

header("Content-length: $file_size")

此标题告诉浏览器文件的大小。有些浏览器需要它才能正确下载文件。无论如何,这是一个很好的方式来告诉文件有多大。这样,下载文件的任何人都可以预测下载需要多长时间。

header("Content-type: $file_type")

此标题告诉浏览器它尝试下载哪种文件。

header("Content-Disposition: attachment; filename=$file_name");

这告诉浏览器以指定的名称保存此下载的文件。如果您不发送此标头,浏览器将尝试使用脚本名称保存文件。

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

答案 1 :(得分:1)

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

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

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;
}

答案 2 :(得分:0)

编辑(强行下载):

function getChart($uniqid= null){
    if($uniqid){

        $image = $uniqid;

        header("Cache-Control: public");
        header("Content-Description: File Transfer");
        header("Content-Disposition: attachment; filename=" . $image);
        header("Content-Type: image/jpg");
        header("Content-Transfer-Encoding: binary");

        readfile($image);

    } else exit;
}

getChart("060620121945.jpg");

试试这个(仅用于渲染图像):

function getChart($uniqid= null){
    if($uniqid){

        $mime_type = "image/png";
        $content = file_get_contents('../webroot/img/'.$uniqid.'.png');
        $base64   = base64_encode($content); 
        return ('data:' . $mime_type . ';base64,' . $base64);

    } else exit;
}

答案 3 :(得分:0)

在致电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;
}

答案 4 :(得分:0)

我多次遇到这个问题。这是我使用的解决方案:

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

因此解决方法是关闭unicode BOM中的download.php

答案 5 :(得分:0)

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

之前