php 导出 大文件,能否支持大文件的输出,或超大内容的输出

该博客介绍了如何使用Workerman框架有效地发送大文件到客户端,同时通过onBufferFull和onBufferDrain事件进行网络拥堵控制。在发送大文件时,文件被存储在本地以避免占用服务器内存,然后根据客户端的网络状况分段加载到内存并发送,以防止内存溢出。通过监听网络拥堵事件,动态调整发送速率,确保稳定且高效的数据传输。
摘要由CSDN通过智能技术生成

建议:

要发送给客户端的数据如果有几个G,这几个G的数据最好存储在本地磁盘上,避免占用服务器内存。然后根据客户端网络数据拥堵情况分段载入内存并发送。

注意:简单的将大文件分段发送不能避免内存爆的问题

假如10个G的文件发送给客户端,客户端接收速度很慢,虽然服务端将10G文件分成多个小文件发送,但是如果客户端接收速度远远低于服务端发送速度,仍然会导致服务端要发送的数据堆积在发送缓冲区中,导致内存爆掉。就像客户端带宽为10k/S,服务端以1M/S的速度发送,仍然会导致数据积压在服务器发送缓冲区导致内存爆掉。

正确的做法应该是根据客户端网络数据拥堵情况控制发送速度。

如何判断客户端网络数据放生拥堵?如何发送?

workerman提供了网络拥堵控制机制,即 onBufferFull和onBufferDrain事件(具体说明参见手册),当服务端向客户端的发送缓冲区满时(缓冲区大小可控制 参见手册)会产生onBufferFull事件,这时服务端应该停止向这个客户端再发送数据(停止从磁盘read数据到内存),因为onBufferFull发生时说明发送给客户端的数据发生拥堵。

而当发送缓冲区的数据全部发送给客户端后(发送缓冲区空了),将会放生onBufferDrain事件,这时服务端可以继续从磁盘read数据,继续向客户端发送。

通过onBufferFull和onBufferDrain事件可以方便控制网络拥堵,既能够减少内存消耗,又能以最快的速度将数据发送给客户端。

示例:

从磁盘发送大文件到客户端参见下面示例(使用的是http协议,其它协议也适用)

use Workerman\Worker;

require_once './Workerman/Autoloader.php';

$worker = new Worker('http://0.0.0.0:4236');

$worker->onMessage = function($connection, $data)

{

if($_SERVER == '/favicon.ico')

{

return $connection->send("HTTP/1.0 404 Not Found\r\nContent-Length: 0\r\n\r\n", true);

}

// 这里发送的是一个大的pdf文件,如果是其它格式的文件,请修改下面代码中http头

send_file($connection, "/your/path/xxx.pdf");

};

function send_file($connection, $file_name)

{

if(!is_file($file_name))

{

$connection->send("HTTP/1.0 404 File Not Found\r\nContent-Length: 18\r\n\r\n404 File Not Found", true);

return;

}

// ======发送http头======

$file_size = filesize($file_name);

$header = "HTTP/1.1 200 OK\r\n";

// 这里写的Content-Type是pdf,如果不是pdf文件请修改Content-Type的值

// mime对应关系参见 https://github.com/walkor/Workerman/blob/master/Protocols/Http/mime.types#L30

$header .= "Content-Type: application/pdf\r\n";

$header .= "Connection: keep-alive\r\n";

$header .= "Content-Length: $file_size\r\n\r\n";

$connection->send($header, true);

// ======分段发送文件内容=======

$connection->fileHandler = fopen($file_name, 'r');

$do_write = function()use($connection)

{

// 对应客户端的连接发送缓冲区未满时

while(empty($connection->bufferFull))

{

// 从磁盘读取文件

$buffer = fread($connection->fileHandler, 8192);

// 读不到数据说明文件读到末尾了

if($buffer === '' || $buffer === false)

{

return;

}

$connection->send($buffer, true);

}

};

// 发生连接发送缓冲区满事件时设置一个标记bufferFull

$connection->onBufferFull = function($connection)

{

// 赋值一个bufferFull临时变量给链接对象,标记发送缓冲区满,暂停do_write发送

$connection->bufferFull = true;

};

// 当发送缓冲区数据发送完毕时触发

$connection->onBufferDrain = function($connection)use($do_write)

{

$connection->bufferFull = false;

$do_write();

};

// 执行发送

$do_write();

}

Worker::runAll();

以上例子亲测ok,请试用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值