#Download CSV error

6 messages · Page 1 of 1 (latest)

grave snow
#

Am trying to download a file I'm creating on the fly (Vue, Laravel, Inertia)

I've got a normal (<a>) link pointing to my download route, however I got this error:

Return value must be of type Illuminate\Http\Response|Illuminate\Http\RedirectResponse|Illuminate\Http\JsonResponse, Symfony\Component\HttpFoundation\StreamedResponse returned

Relevant code:

Controller code

$csv = new CsvGenerator('test');
$csv->addData([
  'first'  => 'never',
  'second' => 'gonna',
  'third'  => 'give',
]);
return response()->streamDownload($csv->getStreamCallback(), $csv->getFilename(), $csv->getHeaders());

The CSV Generator class

class CsvGenerator
  {
    private $filename = 'generic';
    private $headers = [];
    private $data = [];
    private $dataHeaders;

    public function __construct(string $filename) {
      $this->filename = $filename . '-' . now()->timestamp;
      $this->dataHeaders = collect([]);
      $this->headers = [
        'Cache-Control'       => 'must-revalidate, post-check=0, pre-check=0',
        'Content-type'        => 'text/csv',
        'Content-Disposition' => 'attachment; filename=' . $this->filename . '.csv',
        'Expires'             => '0',
        'Pragma'              => 'public',
      ];
    }

    public function getFilename() {
      return $this->filename;
    }

    public function setData(array $data) {
      $this->data = $data;
      foreach ($data as $item) {
        $this->dataHeaders = $this->dataHeaders->union(array_keys($item))->unique();
      }
    }

    public function clearData() {
      $this->data = [];
    }

    public function addData(array $entry) {
      $this->data[] = $entry;
      $this->dataHeaders = $this->dataHeaders->union(array_keys($entry))->unique();
    }

    public function getData() {
      return $this->data;
    }

    public function setDataHeaders(array $headers) {
      $this->dataHeaders = collect($headers);
    }

    public function getDataHeaders() {
      return $this->dataHeaders->toArray();
    }

    public function getHeaders() {
      return $this->headers;
    }

    public function getStreamCallback() {
      $dataHeaders = $this->dataHeaders->toArray();
      $data = $this->data;
      return function () use ($dataHeaders, $data) {
        $handle = fopen("php://output", "w");
        fputcsv($handle, $dataHeaders);
        foreach ($data as $row) {
          fputcsv($handle, $row);
        }
        fclose($handle);
      };
    }

    public function getResponseObject(): StreamedResponse {
      return response()->streamDownload($this->getStreamCallback(), $this->filename, $this->headers);
    }
  }
#

I've done this successfully before on a livewire project with return response()->stream($callback, 200, $headers);

Am using streamDownload this time based on the current docs - https://laravel.com/docs/10.x/responses#streamed-downloads
However I've also tried with ->stream and I get the same error

Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing you to create without sweating the small things.

echo arrow
#

Your controller has a return type of Illuminate\Http\Response|Illuminate\Http\RedirectResponse|Illuminate\Http\JsonResponse while you're returning Symfony\Component\HttpFoundation\StreamedResponse

grave snow
#

Ok, but how do I get it to either be returning the correct response type that's accepted by controllers, or get the controller to accept a streamedresponse?
I can't see any explicit typing on the controller, it's functions, or it's inherited parent, and none of the docs mention anything about having to change controller type to use streamDownload? I can't see any options in the controller artisan creation that seem to suggestion creating a 'streamable' controller either?

echo arrow
#

Show the full code of your controller and the full stack trace. The error is pretty self-explanatory, somewhere there's a return type set to RedirectResponse|JsonResponse, while you're returning a StreamedResponse. The stack trace would show where that's happening

grave snow
#

Controller

<?php

  namespace App\Http\Controllers;

  use App\Ozzy\CsvGenerator;
  use Illuminate\Http\Request;

  class TestController extends Controller
  {
    public function test() {
      // TEMP TESTING
      $csv = new CsvGenerator('test');
      $csv->addData([
        'first'  => 'never',
        'second' => 'gonna',
        'third'  => 'give',
        'fourth' => 'you',
        'fifth'  => 'up',
      ]);

      return $csv->getResponseObject();
    }
  }

routes file

Route::get('/test', [ TestController::class, 'test' ])->name('test');