PHP Slim REST API中的文件从Guzzle调用返回到另一个REST API

问题描述 投票:1回答:1

更新这似乎在某种程度上与输出时读取流有关。 Slim用于输出主体的函数如下所示,其中$ body实现StreamInterface,而$ this-> responseChunkSize为4096:

$amountToRead = $body->getSize();
while ($amountToRead > 0 && !$body->eof()) {
    $length = min($this->responseChunkSize, $amountToRead);
    $data = $body->read($length);
    echo $data;

    $amountToRead -= strlen($data);

    if (connection_status() !== CONNECTION_NORMAL) {
        break;
    }
}

似乎$ body-> eof()调用(这只是PHP的feof()函数的包装器)返回true,即使尚未读取完整文件。不知道为什么会这样。我还验证了如果只对文件执行fopen()并从中创建一个Stream,然后运行相同的代码,则不会发生这种情况。仅在流是通过Guzzle进行的外部REST API调用的产品时才会发生。

原始帖子我有一个使用Slim(v4.4)构建的服务,该服务使用Guzzle(v6.5.3)调用了返回文件的外部REST API。这是在Windows中运行,Web服务器是IIS / FastCGI(我知道,很不寻常)。 PHP版本是7.3.10。从Slim到外部REST API的调用可以很好地检索文件,但是当我的应用程序调用该服务时,某些文件被损坏,根据我在文件大小中看到的内容,似乎有些数据丢失了。从服务到外部REST API的调用非常简单:

$file_response = $guzzleClient->request('GET', "{$base_url}/docs/{$file_id}", [
    'headers'   => [
        'Authorization' => "token {$token}"
    ]
]);

上述调用可以正常工作并正确返回文件,我可以将其显示在屏幕上,也可以使用Guzzle中的'sink'选项保存到文件,它可以正常工作。但是,当我尝试调用包装该调用的服务时,它将失败。我尝试了几件事。首先,我只是按原样返回响应,因为它仍然符合所需的接口。我的Slim路线如下所示:

$app->group('/files', function (Group $group) {
    $group->get('/{file_id}', GetFileAction::class);
});

GetFileAction类具有这样的方法:

public function __invoke(Request $request, Response $response, $args): Response {
    ...Guzzle request returning $file_response here...
    return $file_response;
}

我的应用程序也使用Guzzle来调用服务,呼叫看起来像这样:

$guzzleClient->request(
    'GET',
    "{$base_url}/files/{$file_id}",
    [
        'auth' => [$username, $password],
        'sink' => $file_path
    ]
);

我想知道在Slim中返回Guzzle响应是否可能导致某些意外结果,所以我尝试在服务中返回此结果:

return $response->withBody(new \Slim\Psr7\Stream($file_response->getBody()->detach()));

相同的结果。显然,如果遇到这个完全相同的问题的人可以帮上忙,那将是不错的选择,但如果没有,那么我可以尝试调试流处理的一些建议可能会有所帮助。

php slim guzzle
1个回答
0
投票

我已经确认这与feof()函数返回true的怪异问题有关,即使它没有读取完整的文件。我想出的解决方案涉及创建一个与默认的Slim 4不同(大多数情况相同)的不同的Response Emitter,并覆盖generateBody函数,以便它不依赖于feof()。我是这样的:

$length = min($this->responseChunkSizeCopy, $amountToRead);
while ($amountToRead > 0 && ($data = $body->read($length)) !== false) {
    echo $data;
    $amountToRead -= $length;
    $length = min($this->responseChunkSizeCopy, $amountToRead);

    if (connection_status() !== CONNECTION_NORMAL) {
        break;
    }
}

到目前为止,根据我的测试,此方法效果很好。我不知道为什么feof()不能按预期工作,并且没有真正找到专门解决它的东西。也许这是Windows特有的,并且由于PHP在Windows上不那么普遍,所以它并不常见。但是,请保留此解决方案以防万一。

© www.soinside.com 2019 - 2024. All rights reserved.