使用 hyperf/guzzle
遇到下載下傳檔案的問題, 過程雖波折, 卻頗有意思, 分享給大家.
業務上有一個下載下傳檔案的任務, 太簡單啦
業務上要下載下傳一個 oss 檔案, 假設位址為
oss_url
. 先撸一遍 guzzle 的文檔
下載下傳需要在 request option 中設定 sink
參數: http://docs.guzzlephp.org/en/stable/request-options.html#sink
$oss_url = 'oss_url';
$file_path = 'xxx';
$client = new \GuzzleHttp\Client([
'verify' => false,
'decode_content' => false,
]);
$client->get($oss_url, [
'sink' => $file_path,
]);
換成 hyperf/guzzle 來寫:
// 使用 clientFactory 擷取到協程版 client 即可
$container = ApplicationContext::getContainer();
$clientFactory = $container->get(ClientFactory::class);
$client = $clientFactory->create([
'verify' => false,
'decode_content' => false,
]);
開心的執行, 劇情按照預期的方向發展~
并沒有!!! 下載下傳沒反應!!!
下載下傳位址的問題?
使用 curl/wget 等指令行工具驗證
wget oss_url
curl -O oss_url
下載下傳位址沒問題
檔案有 300M, 會不會是逾時了?
$client = $clientFactory->create([
'timeout' => 600, // 逾時設定為 10 分鐘
'verify' => false,
'decode_content' => false,
]);
噗, 還是一樣沒效果?
翻 hyperf 文檔
hyperf/guzzle: https://doc.hyperf.io/#/zh/guzzle
哦, 原來使用 swoole 配置要這樣:
<?php
use GuzzleHttp\Client;
use Hyperf\Guzzle\CoroutineHandler;
use GuzzleHttp\HandlerStack;
$client = new Client([
'base_uri' => 'http://127.0.0.1:8080',
'handler' => HandlerStack::create(new CoroutineHandler()),
'timeout' => 5,
'swoole' => [ // 看這裡看這裡
'timeout' => 10,
'socket_buffer_size' => 1024 * 1024 * 2,
],
]);
$response = $client->get('/');
這次應該行了吧 !?
噗, 還是一樣沒效果.
直接用 guzzle 行不行
$oss_url = 'oss_url';
$file_path = 'xxx';
$client = new \GuzzleHttp\Client([
'verify' => false,
'decode_content' => false,
]);
$client->get($oss_url, [
'sink' => $file_path,
]);
終于看了需要下載下傳的問題. 是時候刷鍋一波給 hyperf,
什麼渣渣架構, 檔案下載下傳居然都不支援
.
我們看看鍋到底在哪
老規矩, 看源碼, 既然是使用
\Hyperf\Guzzle\ClientFactory->create()
建立的, 看看源碼漲啥樣:
public function create(array $options = []): Client
{
$stack = null;
if (Coroutine::getCid() > 0) {
$stack = HandlerStack::create(new CoroutineHandler());
}
$config = array_replace(['handler' => $stack], $options);
if (method_exists($this->container, 'make')) {
// Create by DI for AOP.
return $this->container->make(Client::class, ['config' => $config]);
}
return new Client($config);
}
協程環境下使用的
new CoroutineHandler
, 來看看廬山真面目(檔案有點長, 不要輕言放棄):
// \Hyperf\Guzzle\CoroutineHandler
// 關鍵在這句, 這裡其實是 \Swoole\Coroutine\Http\Client
$client = new Client($host, $port, $ssl);
原來這裡用的
\Swoole\Coroutine\Http\Client
. 這時候我靈機一動, 會不會是?
來看 swoole 的文檔
下載下傳方法在這裡: https://wiki.swoole.com/wiki/page/938.html
$host = 'www.swoole.com';
$cli = new \Swoole\Coroutine\Http\Client($host, 443, true);
$cli->set(['timeout' => -1]);
$cli->setHeaders([
'Host' => $host,
"User-Agent" => 'Chrome/49.0.2587.3',
'Accept' => '*',
'Accept-Encoding' => 'gzip'
]);
$cli->download('/static/files/swoole-logo.svg', __DIR__ . '/logo.svg');
這 api 和 guzzle 完全不一樣呀 !!! 坑爹呢這是, 用
swoole + hyperf
, 連個檔案下載下傳都搞不定 ?!
swoole + hyperf 表示我這麼強大, 你居然不會用 !
在
swoole v4.4.6的版本更新中, 就增加了對 curl hook 的支援, 添加
SWOOLE_HOOK_FLAGS
即可, hyperf v1.1.0 版本中已經提供了支援:
// bin/hyperf.php:11
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);
開啟後, swoole 就會無縫将 curl hook 為協程版.
無形加速, 最為緻命 !
可是 hyperf/guzzle 預設還是使用的
\Swoole\Coroutine\Http\Client
怎麼辦? 這就太簡單了:
- 如果你喜歡靜态類風格:
<?php
namespace App\Util;
use GuzzleHttp\Client;
class Guzzle
{
/**
* @param array $config
* @return Client
*/
public static function create(array $config)
{
return make(Client::class, ['config' => $config]);
}
}
- 如果你喜歡 ClientFactory 風格
<?php
namespace App\Util;
use GuzzleHttp\Client;
class ClientFactory
{
/**
* @param array $config
* @return Client
*/
public function create(array $config)
{
return make(Client::class, ['config' => $config]);
}
}
寫在最後
沒事不要亂甩鍋, 多問問問題, 多找找原因, 收獲就在這不經意間