天天看點

PHP 輸出緩存機制

      • 緩存原理
      • 下載下傳檔案處理方法

緩存原理

<?php
    ob_start(); //打開緩沖區
    phpinfo(); //使用phpinfo函數
    $info=ob_get_contents(); //得到緩沖區的内容并且指派給$info
    $file=fopen('info.html','w'); //打開檔案info.txt
    fwrite($file,$info); //寫入資訊到info.txt
    fclose($file); //關閉檔案info.txt
?>
           

PHP的輸出緩沖區(轉)

什麼是緩沖區?

簡單而言,緩沖區的作用就是,把輸入或者輸出的内容先放進記憶體,而不顯示或者讀取.至于為什麼要有緩沖區,這是一個很廣泛的問題,如果有興趣,可以在網山找下資料.

其實緩沖區最本質的作用就是,協調高速CPU和相對緩慢的IO裝置(磁盤等)的運作.

PHP在執行的時候,在什麼地方有用到緩沖區?

想要了解PHP的緩沖區,就要知道執行PHP的時候,緩沖區被設定到了什麼地方.

當執行PHP的時候,如果碰到了echo print_r之類的會輸出資料的代碼,PHP就會将要輸出的資料放到PHP自身的緩沖區,等待輸出.

當PHP自身的緩沖區接到指令,訓示要輸出緩沖區的内容時,将會把緩沖區内的資料輸出到apache上, apache接受到PHP輸出的資料,然後再把該資料存在到apache自身的緩沖區内,等到輸出

當apache接受到指令,隻是要輸出緩沖區的内容時, 将會把緩沖區的内容輸出,傳回到浏覽器.

由此可見,PHP要輸出資料的時候,将會經過兩個緩沖區(先是自身的,然後是apache的),再傳回到浏覽器.

緩沖區在PHP中起到什麼作用?

1.最常見的就是在使用header函數之前,就已經輸出了某些資料,這樣會導緻某些錯誤,例如 Cannot modify header information – headers already sent by;

echo "this is test";
header("LOCATION http://www.baidu.com");
           

出現這個錯誤的原因是, 在header之前已經輸出了某些資料,而輸出這些資料的同時, apache将會同時發送一個響應狀态到浏覽器上(既然有輸出,即這個請求是有效的),而其後你又再次使用header函數

發送http頭,則會傳回這個錯誤,錯誤的意思是:HTTP頭已經發送出去了,你不能對他再做修改.

為什麼使用緩沖區可以避免這個錯誤呢?

因為header函數是不受緩沖區影響的,當一碰到header函數的時候,PHP馬上執行apache發送這一個http頭都浏覽器.

而輸出的資料PHP打開輸出緩沖區後, 這些資料将會存放在緩沖區,等待輸出.這樣就可以避免了之前所發生的錯誤.

2.通過PHP寫檔案下載下傳程式的時候.

為了讓檔案下載下傳更安全,同時提高更多的可控性,很多朋友都喜歡用PHP寫檔案下載下傳頁面.其原理很簡單,就是通過fwrite把檔案内容讀出并顯示,然後通過header來發送HTTP頭,讓浏覽器知道這是一個附件,這樣

就可以達到提供下載下傳的效果.

如果用上面的辦法提供下載下傳頁面,會碰到一個效率問題,如果一個檔案很大,假設為100M,那麼在不開啟緩沖區輸出的情況下,必須要把100M資料全部讀出,然後一次傳回到頁面上,如果這樣做,使用者将會在所有資料讀完

之後才會得到響應,降低了使用者體驗感.

如果開啟了輸出緩沖區,當PHP程式讀完檔案的某一段,然後馬上輸出到apache,然後讓apache馬上傳回到浏覽器,這樣就可以減少使用者等待時間.那後面的資料怎麼辦呢?我們可以寫一個while循環,一直一段一段地讀取檔案

每讀一段,就馬上輸出,直到把檔案全部輸出為止,這樣浏覽器就可以持續地接受到資料,而不必等到所有檔案讀取完畢.

另外,該做法還解決了另外一個很嚴重的問題.例如一個檔案是100M,如果不開啟緩沖區的情況下,則需要把100M檔案全部讀入記憶體,然後再輸出.但是,如果PHP程式做了記憶體限制呢?為了保證伺服器的穩定,管理者通常會把PHP的執行

記憶體設一個限制(通過php.ini總的memory_limit, 其預設值是8M), 也就是每個PHP程式使用的記憶體不能使用超過這個值的記憶體. 假設該值為8M,而要讀入的檔案是100M,根本就沒有足夠的記憶體來讀入該檔案.這個時候,我們就需要用到上面的

辦法來解決這個問題,每次隻讀某一段,這樣就可以避免了記憶體的限制

3.靜态檔案緩存

現在很多公司有這麼一個需求, 就是某一個頁面在第一次通路的時候,會執行PHP,然後把顯示的内容傳回到浏覽器,同時需要把這次顯示的内容儲存到伺服器上,這樣下次通路的時候,就直接把儲存在伺服器上的檔案直接顯示,而不需要通過PHP來做操作

這就是所謂的”靜态頁面緩存”.那怎麼樣才能做到把内容傳回到浏覽器的同時把資料儲存到伺服器上呢?這就要用到輸出緩沖區了.

ob_start();
echo 'aaa';
$string = ob_get_contents();
file_put_contents('a.html', $string);
ob_flush();
flush();
           

與輸出緩沖區有關的配置

在PHP.INI中,有兩個跟緩沖區緊密相關的配置項

1.output_buffering

該配置直接影響的是php本身的緩沖區,有3種配置參數.on/off/xK(x為某個整型數值);

on - 開啟緩沖區

off - 關閉緩沖區

256k - 開啟緩沖區,而且當緩沖區的内容超過256k的時候,自動重新整理緩沖區(把資料發送到apache);

2.implicit_flush

該配置直接影響apache的緩沖區,有2種配置參數. on/off

on - 自動重新整理apache緩沖區,也就是,當php發送資料到apache的緩沖區的時候,不需要等待其他指令,直接就把輸出傳回到浏覽器

off - 不自動重新整理apache緩沖區,接受到資料後,等待重新整理指令

與緩沖區有關的函數

1.ob_implicit_flush

作用和implicit_flush一樣,是否自動重新整理apache的緩沖區

2.flush

作用是發送指令到apache,讓apache重新整理自身的輸出緩沖區.

3.ob_start

打開輸出緩沖區,無論php.ini的檔案如何配置,如果使用該函數,即使output_buffering設定成off,也會打開輸出緩沖區

ob_start函數還接受一個參數,該參數是一個函數的回調,意思是,在輸入緩沖區内容之前,需要使用調用傳遞進來的參數把緩沖區的内容處理一次,再放入緩沖區内

4.ob_flush

訓示php本身重新整理自身的緩沖區,把資料發送到apache

5.ob_clean

清除php緩沖區裡面的内容

6.ob_end_clean

清除php緩沖區内的内容,并且關閉輸出緩沖區

7.ob_end_flush

把php自身的緩沖區裡的内容發送到apache,并把清除自身緩沖區内的内容

8.ob_get_clean

擷取緩沖區的内容之後,清除緩沖區.

9.ob_get_contents

擷取輸出緩沖區裡的内容

10.ob_get_flush

擷取緩沖區裡的内容,并且把這些内容發送到apache

11.ob_get_length

擷取緩沖區裡内容的長度

12.ob_list_handlers

擷取運作ob_start時,所回調的函數名稱, 例如:

ob_start(‘ob_gzhandler’);

print_r(ob_list_handlers);

将列印出ob_gzhandler;

13.ob_gzhandler

該函數的作用是作為ob_start的回調參數, 在緩沖區重新整理之前,會調用該函數對資料進行到底gzip或者deflate壓縮.這個函數需要zlib擴充的支援.

使用緩沖區的相關内容

1.ob_flush和flush的次序關系.上面的分析可以看出,ob_flush是和php自身相關的,而flush操作的是apache的緩沖區,所有我們在使用這兩個函數的時候,需要先執行ob_flush,

再執行flush,因為我們需要先把資料從PHP上發送到apache,然後再由apache傳回到浏覽器.如果php還沒有把資料重新整理到apache,就調用了flush,則apache無任何資料傳回到浏覽器.

2.有的浏覽器,如果接受到的字元太少,則不會把資料顯示出來,例如老版的IE(必須要大于256k才顯示).這樣就會造成一個疑問, 明明在php和apache都進行了重新整理緩沖區的操作,但是浏覽器就是沒有出現自己想要的資料,也許就是這個原因造成的.是以才測試的時候,可以在輸出資料的後面加上多個空格,以填滿資料,确定不會浏覽器造成這類詭異的問題.

3.有些webserver,他自身的輸出緩沖區會有一些限制,比如nginx,他有一個配置fastcgi_buffer_size 4k, 就是是表明,當自身的輸出緩沖區的内容達到4K才會重新整理,是以為了保證内容的資料,可以添加以下代碼,保證内容長度

<?php

echo str_repeat(" ",);

?>
           

4.在apache中,如果你開啟了mod_gzip的壓縮子產品,這樣可能會導緻你的flush函數重新整理不成功,其原因是,mod_gzip有自己的輸出緩沖區,當php執行了flush函數,訓示apache重新整理輸出緩沖區,但是内容需要壓縮,apache就把内容輸出到自身的mod_gzip子產品,mod_gzip也有自身的輸出 緩沖區,他也不會馬上輸出,是以造成了内容不能馬上輸出.為了改善這個情況,可以關閉mod_gzip子產品,或者在httpd.conf增加以下内容,以禁止壓縮

原文連結:http://www.keepmyway.com/index.php/124.html

下載下傳檔案處理方法

/**
     * 下載下傳檔案處理方法
     * @param unknown $path
     * @param unknown $fileName
     * @return boolean
     */
    public static function download($path, $fileName){

        $path = trim($path);
        $fileName = trim($fileName);

        //檔案名轉碼
        $fileName=iconv('utf-8','gb2312',$fileName);

        //設定路徑
        if ( substr($path, strlen($path)-, strlen($path)) != '/' ){

            $file = $_SERVER['DOCUMENT_ROOT'].$path.'/'.$fileName;

        }else{

            $file = $_SERVER['DOCUMENT_ROOT'].$path.$fileName;

        }
        //die($file);
        file_exists($file) || die('下載下傳檔案不存在');

        $file_size = filesize($file);

        //讀取檔案
        $fp = fopen($file,"r");
        $buffer_size = ;
        $cur_pos = ;

        header("Content-type: application/octet-stream");
        header("Accept-Ranges: bytes");
        header("Accept-Length: $file_size");
        header("Content-Disposition: attachment; filename=".$fileName);

        //刷出緩沖區
        ob_clean();
        flush();

        //輸出檔案
        while( !feof($fp) && $file_size - $cur_pos > $buffer_size )
        {
            $buffer = fread($fp,$buffer_size);

            echo $buffer;

            $cur_pos += $buffer_size;
        }

        $buffer = fread($fp,$file_size-$cur_pos);

        echo $buffer;

        fclose($fp);

        return true;


    }