Загрузка файлов Laravel 5: stream() или загрузка()

У меня есть приложение Laravel 5.4, где аутентифицированные пользователи должны иметь возможность загружать личные файлы из хранилища S3. Я установил маршрут и контроллер, чтобы разрешить приватную загрузку файлов.

Код выглядит следующим образом:

Маршрут:

Route::get('file/{filename}', '[email protected]')->where(['filename' => '[A-Za-z0-9-._\/]+'])->name('file')->middleware('auth:employee');

Контроллер:

public function download($fileName)
{
    if (!$fileName || !Storage::exists($fileName)) {
        abort(404);
    }

    return response()->stream(function() use ($fileName) {
        $stream = Storage::readStream($fileName);
        fpassthru($stream);
        if (is_resource($stream)) {
            fclose($stream);
        }
    }, 200, [
        'Cache-Control'         => 'must-revalidate, post-check=0, pre-check=0',
        'Content-Type'          => Storage::mimeType($fileName),
        'Content-Length'        => Storage::size($fileName),
        'Content-Disposition'   => 'attachment; filename="' . basename($fileName) . '"',
        'Pragma'                => 'public',
    ]);
}

Все работает нормально, но когда я ближе посмотрел на Laravel docs, я обнаружил, что они просто говорят о response()->download().

Если я реализую такой ответ, мой код будет выглядеть так:

public function download($fileName)
{
    if (!$fileName || !Storage::exists($fileName)) {
        abort(404);
    }

    $file = Storage::get($fileName);

    return response()->download($file, $fileName, [
        'Content-Type'  => Storage::mimeType($fileName),
    ]);
}

Обе функции можно найти в API-документах.

Мой вопрос: что было бы предпочтительным способом, и каковы преимущества/недостатки каждого?

Из того, что я собрал до сих пор:

Поток:

  • Не требует загрузки всего файла в память
  • Подходит для больших файлов

Скачать

  • Требуется меньше кода

Ответ 1

Когда вы вызываете Laravel помощником response(), он возвращает экземпляр Illuminate\Routing\ResponseFactory. У ResponseFactory есть эти два метода: download и stream - два рассматриваемых метода. Если вы покопаетесь немного глубже, вы увидите, что download возвращает экземпляр \Symfony\Component\HttpFoundation\BinaryFileResponse, а stream возвращает \Symfony\Component\HttpFoundation\StreamedResponse - это оба компонента Symfony.

Копаться в коде здесь не нужно, но приятно иметь представление о том, что происходит под капотом. Теперь, когда мы знаем, что возвращаемые базовые объекты взяты из HTTP-компонента Symfony, мы можем обратиться к документации Symfony и посмотреть, что они рекомендуют использовать. Обычно потоки используются, когда размер файла неизвестен, например, когда вы генерируете файл на лету. В большинстве других случаев BinaryFileResponse, сгенерированный методом download, будет достаточно для ваших нужд.

Вы можете взглянуть на более подробное объяснение потоковой передачи HTTP и ее вариантов использования здесь.