婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > 詳解PHP如何讀取大文件

詳解PHP如何讀取大文件

熱門標簽:合肥ai電銷機器人費用 溫州外呼系統招商 滄州電銷外呼系統價格 凱立德地鐵站地圖標注 手機外呼系統什么原理 銀行信貸電話機器人 上海400客服電話怎么申請 400電話個人能不能辦理 天津電銷外呼系統違法嗎

衡量成功

唯一能確認我們對代碼所做改進是否有效的方式是:衡量一個糟糕的情況,然后對比我們已經應用改進后的衡量情況。換言之,除非我們知道 “解決方案” 能幫我們到什么程度 (如果有的話),否則我們并不知道它是否是一個解決方案。

我們可以關注兩個指標。首先是 CPU 使用率。我們要處理的過程運行得有多快或多慢?其次是內存使用率。腳本執行要占用多少內存?這些通常是成反比的 — 這意味著我們能夠以 CPU 使用率為代價減少內存的使用率,反之亦可。

在一個異步處理模型 (例如多進程或多線程 PHP 應用程序) 中,CPU 和內存使用率都是重要的考量。在傳統 PHP 架構中,任一達到服務器所限時這些通常都會成為一個麻煩。

測量 PHP 內部的 CPU 使用率是難以實現的。如果你確實關注這一塊,可用考慮在 Ubuntu 或 macOS 中使用類似于 top 的命令。對于 Windows,則可用考慮使用 Linux 子系統,這樣你就能夠在 Ubuntu 中使用 top 命令了。

在本教程中,我們將測量內存使用情況。我們將看一下 “傳統” 腳本會使用多少內存。我們也會實現一些優化策略并對它們進行度量。最后,我希望你能做一個合理的選擇。

以下是我們用于查看內存使用量的方法:

// formatBytes 方法取材于 php.net 文檔
memory_get_peak_usage();
function formatBytes($bytes, $precision = 2) {
    $units = array("b", "kb", "mb", "gb", "tb");
    $bytes = max($bytes, 0);
    $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
    $pow = min($pow, count($units) - 1);
    $bytes /= (1  (10 * $pow));
    return round($bytes, $precision) . " " . $units[$pow];
}

我們將在腳本的結尾處使用這些方法,以便于我們了解哪個腳本一次使用了最多的內存。

我們有什么選擇?

我們有許多方法來有效地讀取文件。有以下兩種場景會使用到他們。我們可能希望同時讀取和處理所有數據,對處理后的數據進行輸出或者執行其他操作。 我們還可能希望對數據流進行轉換而不需要訪問到這些數據。

想象以下,對于第一種情況,如果我們希望讀取文件并且把每 10,000 行的數據交給單獨的隊列進行處理。我們則需要至少把 10,000 行的數據加載到內存中,然后把它們交給隊列管理器(無論使用哪種)。

對于第二種情況,假設我們想要壓縮一個 API 響應的內容,這個 API 響應特別大。雖然這里我們不關心它的內容是什么,但是我們需要確保它被以一種壓縮格式備份起來。

這兩種情況,我們都需要讀取大文件。不同的是,第一種情況我們需要知道數據是什么,而第二種情況我們不關心數據是什么。接下來,讓我們來深入討論一下這兩種做法.

逐行讀取文件

PHP 處理文件的函數很多,讓我們將其中一些函數結合起來實現一個簡單的文件閱讀器

// from memory.php
function formatBytes($bytes, $precision = 2) {
    $units = array("b", "kb", "mb", "gb", "tb");
    $bytes = max($bytes, 0);
    $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
    $pow = min($pow, count($units) - 1);
    $bytes /= (1  (10 * $pow));
    return round($bytes, $precision) . " " . $units[$pow];
}
print formatBytes(memory_get_peak_usage());
// from reading-files-line-by-line-1.php
function readTheFile($path) {
    $lines = [];
    $handle = fopen($path, "r");
    while(!feof($handle)) {
        $lines[] = trim(fgets($handle));
    }
    fclose($handle);
    return $lines;
}
readTheFile("shakespeare.txt");
require "memory.php";

我們正在閱讀一個包括莎士比亞全部著作的文本文件。該文件大小大約為 5.5 MB。內存使用峰值為 12.8 MB。現在,讓我們使用生成器來讀取每一行:

// from reading-files-line-by-line-2.php
function readTheFile($path) {
    $handle = fopen($path, "r");
    while(!feof($handle)) {
        yield trim(fgets($handle));
    }
    fclose($handle);
}
readTheFile("shakespeare.txt");
require "memory.php";

文件大小相同,但是內存使用峰值為 393 KB。這個數據意義大不大,因為我們需要加入對文件數據的處理。例如,當出現兩個空白行時,將文檔拆分為多個塊:

// from reading-files-line-by-line-3.php
$iterator = readTheFile("shakespeare.txt");
$buffer = "";
foreach ($iterator as $iteration) {
    preg_match("/\n{3}/", $buffer, $matches);
    if (count($matches)) {
        print ".";
        $buffer = "";
    } else {
        $buffer .= $iteration . PHP_EOL;
    }
}
require "memory.php";

有人猜測這次使用多少內存嗎?即使我們將文本文檔分為 126 個塊,我們仍然只使用 459 KB 的內存。鑒于生成器的性質,我們將使用的最大內存是在迭代中需要存儲最大文本塊的內存。在這種情況下,最大的塊是 101985 個字符。

生成器還有其他用途,但顯然它可以很好的讀取大型文件。如果我們需要處理數據,生成器可能是最好的方法。

文件之間的管道

在不需要處理數據的情況下,我們可以將文件數據從一個文件傳遞到另一個文件。這通常稱為管道 (大概是因為除了兩端之外,我們看不到管道內的任何東西,當然,只要它是不透明的)。我們可以通過流 (stream) 來實現,首先,我們編寫一個腳本實現一個文件到另一個文件的傳輸,以便我們可以測量內存使用情況:

// from piping-files-1.php
file_put_contents(
    "piping-files-1.txt", file_get_contents("shakespeare.txt")
);
require "memory.php";

結果并沒有讓人感到意外。該腳本比其復制的文本文件使用更多的內存來運行。這是因為腳本必須在內存中讀取整個文件直到將其寫入另外一個文件。對于小的文件而言,這種操作是 OK 的。但是將其用于大文件時,就不是那么回事了。

讓我們嘗試從一個文件流式傳輸 (或管道傳輸) 到另一個文件:

// from piping-files-2.php
$handle1 = fopen("shakespeare.txt", "r");
$handle2 = fopen("piping-files-2.txt", "w");
stream_copy_to_stream($handle1, $handle2);
fclose($handle1);
fclose($handle2);
require "memory.php";

這段代碼有點奇怪。我們打開兩個文件的句柄,第一個處于讀取模式,第二個處于寫入模式。然后,我們從第一個復制到第二個。我們通過再次關閉兩個文件來完成。當你知道內存使用為 393 KB 時,可能會感到驚訝。這個數字看起來很熟悉,這不就是利用生成器保存逐行讀取內容時所使用的內存嗎。這是因為fgets的第二個參數定義了每行要讀取的字節數 (默認為-1或到達新行之前的長度)。stream_copy_to_stream 的第三個參數是相同的(默認值完全相同)。stream_copy_to_stream 一次從一個流讀取一行,并將其寫入另一流。由于我們不需要處理該值,因此它會跳過生成器產生值的部分

單單傳輸文字還不夠實用,所以考慮下其他例子。假設我們想從 CDN 輸出圖像,可以用以下代碼來描述

// from piping-files-3.php
file_put_contents(
    "piping-files-3.jpeg", file_get_contents(
        "https://github.com/assertchris/uploads/raw/master/rick.jpg"
    )
);
// ...or write this straight to stdout, if we don't need the memory info
require "memory.php";

想象一下應用程度執行到該步驟。這次我們不是要從本地文件系統中獲取圖像,而是從 CDN 獲取。我們用 file_get_contents 代替更優雅的處理方式 (例如 Guzzle),它們的實際效果是一樣的。

內存使用情況為 581KB,現在,我們如何嘗試進行流傳輸呢?

// from piping-files-4.php
$handle1 = fopen(
"https://github.com/assertchris/uploads/raw/master/rick.jpg", "r"
);
$handle2 = fopen(
"piping-files-4.jpeg", "w"
);
// ...or write this straight to stdout, if we don't need the memory info
stream_copy_to_stream($handle1, $handle2);
fclose($handle1);
fclose($handle2);
require "memory.php";

內存使用比剛才略少 (400 KB),但是結果是相同的。如果我們不需要內存信息,也可以打印至標準輸出。PHP 提供了一種簡單的方法來執行此操作:

$handle1 = fopen(
"https://github.com/assertchris/uploads/raw/master/rick.jpg", "r"
);
$handle2 = fopen(
"php://stdout", "w"
);
stream_copy_to_stream($handle1, $handle2);
fclose($handle1);
fclose($handle2);
// require "memory.php";

其他流

還存在一些流可以通過管道來讀寫。

  • php://stdin只讀
  • php://stderr只寫,與php://stdout相似
  • php://input只讀,使我們可以訪問原始請求內容
  • php://output只寫,可讓我們寫入輸出緩沖區
  • php://memory與php://temp(可讀寫) 是臨時存儲數據的地方。區別在于數據足夠大時php:/// temp就會將數據存儲在文件系統中,而php:/// memory將繼續存儲在內存中直到耗盡。

過濾器

我們可以對流使用另一個技巧,稱為過濾器。它介于兩者之間,對數據進行了適當的控制使其不暴露給外接。假設我們要壓縮shakespeare.txt文件。我們可以使用 Zip 擴展

// from filters-1.php
$zip = new ZipArchive();
$filename = "filters-1.zip";
$zip->open($filename, ZipArchive::CREATE);
$zip->addFromString("shakespeare.txt", file_get_contents("shakespeare.txt"));
$zip->close();
require "memory.php";

這段代碼雖然整潔,但是總共使用了大概 10.75 MB 的內存。我們可以使用過濾器來進行優化

// from filters-2.php
$handle1 = fopen(
"php://filter/zlib.deflate/resource=shakespeare.txt", "r"
);
$handle2 = fopen(
"filters-2.deflated", "w"
);
stream_copy_to_stream($handle1, $handle2);
fclose($handle1);
fclose($handle2);
require "memory.php";

在這里,我們可以看到php:///filter/zlib.deflate過濾器,該過濾器讀取和壓縮資源的內容。然后我們可以將該壓縮數據通過管道傳輸到另一個文件中。這僅使用了 896KB 內存。

雖然格式不同,或者說使用 zip 壓縮文件有其他諸多好處。但是,你不得不考慮:如果選擇其他格式你可以節省 12 倍的內存,你會不會心動?

要對數據進行解壓,只需要通過另外一個 zlib 過濾器:

// from filters-2.php
file_get_contents(
    "php://filter/zlib.inflate/resource=filters-2.deflated"
);

自定義流

fopen和file_get_contents具有它們自己的默認選項集,但是它們是完全可定制的。要定義它們,我們需要創建一個新的流上下文

// from creating-contexts-1.php
$data = join("", [
    "twitter=assertchris",
]);
$headers = join("\r\n", [
    "Content-type: application/x-www-form-urlencoded",
    "Content-length: " . strlen($data),
]);
$options = [
    "http" => [
        "method" => "POST",
        "header"=> $headers,
        "content" => $data,
    ],
];
$context = stream_content_create($options);
$handle = fopen("https://example.com/register", "r", false, $context);
$response = stream_get_contents($handle);
fclose($handle);

本例中,我們嘗試發送一個 POST 請求給 API。API 端點是安全的,不過我們仍然使用了 http 上下文屬性(可用于 http 或者 https)。我們設置了一些頭部,并打開了 API 的文件句柄。我們可以將句柄以只讀方式打開,上下文負責編寫。

創建自定義協議和過濾器

在總結之前,我們先談談創建自定義協議。

Protocol {
    public resource $context;
    public __construct ( void )
    public __destruct ( void )
    public bool dir_closedir ( void )
    public bool dir_opendir ( string $path , int $options )
    public string dir_readdir ( void )
    public bool dir_rewinddir ( void )
    public bool mkdir ( string $path , int $mode , int $options )
    public bool rename ( string $path_from , string $path_to )
    public bool rmdir ( string $path , int $options )
    public resource stream_cast ( int $cast_as )
    public void stream_close ( void )
    public bool stream_eof ( void )
    public bool stream_flush ( void )
    public bool stream_lock ( int $operation )
    public bool stream_metadata ( string $path , int $option , mixed $value )
    public bool stream_open ( string $path , string $mode , int $options ,
        string $opened_path )
    public string stream_read ( int $count )
    public bool stream_seek ( int $offset , int $whence = SEEK_SET )
    public bool stream_set_option ( int $option , int $arg1 , int $arg2 )
    public array stream_stat ( void )
    public int stream_tell ( void )
    public bool stream_truncate ( int $new_size )
    public int stream_write ( string $data )
    public bool unlink ( string $path )
    public array url_stat ( string $path , int $flags )
}

我們并不打算實現其中一個,因為我認為它值得擁有自己的教程。有很多工作要做。但是一旦完成工作,我們就可以很容易地注冊流包裝器:

if (in_array("highlight-names", stream_get_wrappers())) {
    stream_wrapper_unregister("highlight-names");
}
stream_wrapper_register("highlight-names", "HighlightNamesProtocol");
$highlighted = file_get_contents("highlight-names://story.txt");

同樣,也可以創建自定義流過濾器。

Filter {
    public $filtername;
    public $params
    public int filter ( resource $in , resource $out , int $consumed ,
        bool $closing )
    public void onClose ( void )
    public bool onCreate ( void )
}

可被輕松注冊

$handle = fopen("story.txt", "w+");
stream_filter_append($handle, "highlight-names", STREAM_FILTER_READ);

highlight-names 需要與新過濾器類的 filtername 屬性匹配。還可以在 php:///filter/highligh-names/resource=story.txt 字符串中使用自定義過濾器。定義過濾器比定義協議要容易得多。原因之一是協議需要處理目錄操作,而過濾器僅需要處理每個數據塊。

如果您愿意,我強烈建議您嘗試創建自定義協議和過濾器。如果您可以將過濾器應用于 stream_copy_to_stream 操作,則即使處理令人討厭的大文件,您的應用程序也將幾乎不使用任何內存。想象一下編寫調整大小圖像過濾器或加密應用程序過濾器。

如果你愿意,我強烈建議你嘗試創建自定義協議和過濾器。如果你可以將過濾器應用于 stream_copy_to_stream 操作,即使處理煩人的大文件,你的應用程序也幾乎不使用任何內存。想象下編寫 resize-image 過濾器和 encrypt-for-application 過濾器吧。

總結

雖然這不是我們經常遇到的問題,但是在處理大文件時的確很容易搞砸。在異步應用中,如果我們不注意內存的使用情況,很容易導致服務器的崩潰。

本教程希望能帶給你一些新的想法(或者更新你的對這方面的固有記憶),以便你能夠更多的考慮如何有效地讀取和寫入大文件。當我們開始熟悉和使用流和生成器并停止使用諸如 file_get_contents 這樣的函數時,這方面的錯誤將全部從應用程序中消失,這不失為一件好事。

以上就是詳解PHP如何讀取大文件的詳細內容,更多關于PHP如何讀取大文件的資料請關注腳本之家其它相關文章!

您可能感興趣的文章:
  • PHP大文件分割分片上傳實現代碼
  • PHP大文件及斷點續傳下載實現代碼
  • php實現斷點續傳大文件示例代碼
  • PHP下載大文件失敗并限制下載速度的實例代碼
  • PHP超低內存遍歷目錄文件和讀取超大文件的方法
  • 詳解PHP多個進程配合redis的有序集合實現大文件去重
  • PHP如何通過表單直接提交大文件詳解
  • PHP大文件分片上傳的實現方法
  • php下載遠程大文件(獲取遠程文件大小)的實例

標簽:怒江 酒泉 溫州 白城 七臺河 洛陽 赤峰 金華

巨人網絡通訊聲明:本文標題《詳解PHP如何讀取大文件》,本文關鍵詞  詳解,PHP,如何,讀取,大,文件,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《詳解PHP如何讀取大文件》相關的同類信息!
  • 本頁收集關于詳解PHP如何讀取大文件的相關信息資訊供網民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    五月天视频一区| 国产毛片精品国产一区二区三区| 亚洲最快最全在线视频| 91同城在线观看| 亚洲日本成人在线观看| 色丁香久综合在线久综合在线观看| 国产精品嫩草99a| 91蜜桃在线观看| 一区二区三区四区不卡视频 | 欧美国产综合一区二区| 国产一区二区视频在线| 久久色在线观看| 成人高清免费观看| 中文字幕亚洲一区二区va在线| 99国产一区二区三精品乱码| 亚洲自拍欧美精品| 欧美日韩精品欧美日韩精品一| 亚洲一区免费观看| 精品少妇一区二区三区| 国产成人综合自拍| 一区二区三区**美女毛片| 欧美色图一区二区三区| 久久国产视频网| 国产精品第四页| 欧美日韩国产精选| 粉嫩高潮美女一区二区三区| 亚洲成人免费在线观看| 欧美一级片免费看| 粉嫩13p一区二区三区| 亚洲美女淫视频| 欧美一区三区二区| 成人国产精品免费观看视频| 亚洲成av人片一区二区三区| 26uuu国产在线精品一区二区| 91亚洲国产成人精品一区二区三 | 色综合久久66| 国产成人日日夜夜| 免费av成人在线| 依依成人精品视频| 国产目拍亚洲精品99久久精品| 91精品在线免费观看| 色美美综合视频| 国产91在线看| 青青草97国产精品免费观看| 亚洲男人的天堂av| 国产精品丝袜久久久久久app| 欧美一区二区三区四区视频| 在线观看国产日韩| 成人av影院在线| 国产成人啪午夜精品网站男同| 日本欧美久久久久免费播放网| 亚洲欧美日韩电影| 亚洲人妖av一区二区| 国产欧美日韩在线观看| 久久亚洲私人国产精品va媚药| 欧美一区二区三区男人的天堂| 欧美日韩一区二区在线视频| 日本乱人伦aⅴ精品| 91在线视频官网| 99热精品一区二区| 成人av电影免费观看| 高清在线成人网| 国产精品夜夜爽| 成a人片亚洲日本久久| 成人精品视频一区二区三区 | 精品日韩在线一区| 日韩一级黄色大片| 日韩欧美亚洲国产另类| 91麻豆精品国产91久久久更新时间 | 午夜精品一区二区三区免费视频 | 国产精品国产三级国产专播品爱网| 久久综合色播五月| 国产日产欧美一区| 国产欧美精品一区二区色综合| 国产精品午夜电影| 亚洲视频图片小说| 午夜av区久久| 久久黄色级2电影| 国产传媒一区在线| www.欧美色图| 欧美三级乱人伦电影| 欧美一级艳片视频免费观看| 欧美精品一区二区蜜臀亚洲| 国产欧美一区二区精品仙草咪| 中文字幕一区二区三中文字幕| 亚洲欧美日韩一区| 天天操天天综合网| 国产黄色91视频| 91麻豆国产自产在线观看| 欧美精品日日鲁夜夜添| www精品美女久久久tv| 亚洲摸摸操操av| 免费在线观看一区二区三区| 国产成人午夜精品影院观看视频| 一本大道久久a久久精品综合| 欧洲精品中文字幕| 精品国产乱码久久久久久牛牛| 综合在线观看色| 久久国产精品色婷婷| 91污片在线观看| 精品剧情v国产在线观看在线| 国产精品天美传媒| 日产欧产美韩系列久久99| 国产成人精品一区二| 欧美在线制服丝袜| 久久综合狠狠综合| 日日夜夜精品视频天天综合网| 中文在线免费一区三区高中清不卡| 中文字幕一区二区不卡 | 中文字幕一区二区不卡| 亚洲h在线观看| 国产精品 日产精品 欧美精品| 欧美日韩精品一二三区| 亚洲国产精品精华液2区45| 色先锋aa成人| 亚洲午夜私人影院| 欧美精品在线一区二区| 日本不卡一区二区三区| 久久久国产综合精品女国产盗摄| 成人午夜视频福利| 亚洲精品菠萝久久久久久久| 欧美老女人在线| 国产精品香蕉一区二区三区| 日韩一区中文字幕| 91精品国产综合久久久久久久| 久久99国内精品| 中文久久乱码一区二区| 在线观看www91| 国产尤物一区二区在线| 中文字幕一区二区三区在线播放 | 91麻豆自制传媒国产之光| 午夜精品成人在线视频| 久久综合色鬼综合色| 色噜噜狠狠色综合欧洲selulu| 日韩av中文字幕一区二区三区| 久久久亚洲午夜电影| 欧美亚洲国产一区二区三区| 经典一区二区三区| 亚洲影视资源网| 国产三级一区二区三区| 欧美女孩性生活视频| eeuss鲁片一区二区三区| 麻豆精品新av中文字幕| 一区二区三区国产精品| 久久久久国色av免费看影院| 欧美日本精品一区二区三区| 成人av小说网| 国产一区二区三区在线观看精品 | 日韩午夜三级在线| 不卡一区在线观看| 久久99精品久久久久| 一区二区三区在线视频观看58| 欧美成人vps| 欧美精品日韩一本| 色欧美乱欧美15图片| 成人高清视频在线| 国产成人午夜电影网| 精品在线播放午夜| 免费成人你懂的| 天堂va蜜桃一区二区三区漫画版| 亚洲免费视频成人| 国产精品女主播av| 国产日韩一级二级三级| 日韩视频一区二区三区在线播放| 欧美三级视频在线| 欧美日韩亚洲综合一区二区三区| 91性感美女视频| 波多野结衣一区二区三区| 国产99久久精品| 国产成人午夜视频| 成人深夜视频在线观看| 国产精品白丝jk黑袜喷水| 欧美激情一二三区| 欧美日韩黄色一区二区| 国产久卡久卡久卡久卡视频精品| 最新高清无码专区| 粉嫩高潮美女一区二区三区| 亚洲1区2区3区视频| 亚洲午夜激情av| 亚洲成人免费影院| 视频一区在线播放| 麻豆精品视频在线观看免费 | 亚洲国产精品高清| 国产欧美va欧美不卡在线| 国产日产欧美精品一区二区三区| 久久综合网色—综合色88| 国产欧美日韩另类一区| 国产精品护士白丝一区av| 椎名由奈av一区二区三区| 国产精品美女久久久久久| 亚洲三级理论片| 性做久久久久久免费观看欧美| 五月综合激情婷婷六月色窝| 免费成人美女在线观看| 国产综合色产在线精品| 成a人片国产精品| 欧美伊人久久久久久久久影院| 欧美日韩不卡一区| 日韩欧美在线网站| 国产精品全国免费观看高清|