Файл возврата в PHP Slim REST API из вызова Guzzle к другому REST API - PullRequest
1 голос
/ 21 апреля 2020

Обновление
Похоже, это каким-то образом связано с чтением потока при выводе. Функция, используемая 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 () для файла и создаю из него поток, а затем запускаю тот же код. Это происходит только тогда, когда поток является продуктом внешнего вызова REST API через Guzzle.

Исходное сообщение
У меня есть служба, созданная с использованием Slim (v4.4), которая вызывает внешний REST API с использованием Guzzle (v6.5.3), который возвращает файл. Это работает в Windows, веб-сервер IIS / FastCGI (я знаю, необычно). PHP версия 7.3.10. При обращении Slim к внешнему REST API файл извлекается просто отлично, но когда мое приложение вызывает службу, некоторые файлы повреждаются, кажется, что некоторые данные теряются в зависимости от того, что я вижу в размере файла. Вызов службы из внешнего API REST довольно прост:

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

Вышеуказанный вызов работает нормально и возвращает файл правильно, я могу либо отобразить его на экране, либо использовать опцию 'sink' в Guzzle чтобы сохранить в файл, он работает нормально. Но когда я пытаюсь вызвать сервис, который оборачивает этот вызов, он терпит неудачу. Я попробовал пару вещей. Во-первых, я просто возвращал ответ как есть, поскольку он в любом случае соответствует требуемому интерфейсу. Мой 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
    ]
);

Мне было интересно, может ли возврат ответа Guzzle в Slim вызвать какой-то неожиданный результат, поэтому я попытался вернуть его в службу:

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

Тот же результат. Очевидно, что если кто-то, кто столкнулся с точно такой же проблемой, может помочь, это было бы здорово, но если бы не несколько указаний о том, как я мог бы попытаться отладить обработку потоков, вероятно, было бы полезно.

1 Ответ

0 голосов
/ 22 апреля 2020

Я подтвердил, что это связано со странной проблемой с функцией feof (), возвращающей true, даже если она не прочитала полный файл. Решение, которое я придумала, заключалось в создании ответного излучателя, отличного от стандартного Slim 4 (в основном, такого же), и перезаписи функции emitBody, чтобы она не зависела от 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 конкретная c вещь, и, поскольку PHP менее распространена на Windows, это не обычное явление. Но оставив это решение здесь, на случай, если оно кому-нибудь поможет.

...