Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

FareedR's avatar

Return response as a download using Spatie/Browsershot

Anyone using spatie/browsershot ? everything works . pdf file stored on storage. but for the frontend part. how can i return success with automatically download the pdf ?

public function stock_custom($from,$to)
    {
        $stocks = Stock::with(['analyses' => function ($query){
            $query->whereNotNull('time_frame')->whereNotNull('description');
        }])->whereBetween('created_at',[$from.' 00:00:00', $to.' 23:59:59'])->get();

        return view('stocks.custom',compact('from','to','stocks'));
    }

public function get_download_custom(Request $request)
    {
        $from   = $request->get('from');
        $to     = $request->get('to');

        Browsershot::url(route('stock.retrieve.custom', [$from,$to]))
        ->setNodeBinary(env('NODE_PATH'))
        ->setNpmBinary(env('NPM_PATH'))
        ->setOption('landscape', true)
        ->margins(10, 10, 10, 10)
        ->showBackground()
        ->emulateMedia("screen")
        ->format('A4')
        ->save(storage_path('app/public').'/'."stock_".$from.'_'.$to.'.pdf');

        $file_name = storage_path('app/public').'/'."stock_".$from.'_'.$to.'.pdf';
 
        return \Storage::disk('public')->download("stock_".$from.'_'.$to.'.pdf');
    }

//blade javascript

	swal({
                title: "Are you sure?",
                text: "Confirm with your input?",
                icon: "warning",
                buttons: true,
                dangerMode: true,
            })
            .then((willUpdate) => {
                if (willUpdate) {
                    $.ajax({
                        url : "{{ url('stocks-custom') }}",
                        type : "POST",
                        data : {
                            '_method' : 'POST',
                            'from': from,
                            'to': to
                        },
                        success: function(){
                            // i've no idea what to put here. 
                        },
                        error : function(){
                            swal({
                                title: 'Opps...',
                                text : data.message,
                                type : 'error',
                                timer : '1500'
                            })
                        }
                    })
                } else {
                    //
                }
            });
0 likes
5 replies
willvincent's avatar

Since you're using ajax, basically, you need to inject an element into the dom, and automatically trigger a click on it to initiate the download.

A helper function like this is useful for that:

export default function downloadFile(data, { filename, mimeType } = {}) {
  const link = document.createElement('a');
  const blob = new Blob([data], {
      mimeType,
  });

  link.style.display = 'none';
  link.href = window.URL.createObjectURL(blob);
  link.download = filename;
  document.body.appendChild(link);
  link.click();

  // Some browsers may fail to download if the link is removed too quickly
  setTimeout(() => {
    document.body.removeChild(link);
  }, 100);
}

To use just pass your response data, the desired filename to send it as, and the appropriate mimetype to the function, it'll inject an anchor link to the dom, click it, and then 100ms later remove the link.

success: function(data) {
    downloadFile(data, {
      filename: `Your-desired-filename.pdf`,
      mimeType: 'application/pdf',
    });
},

You'll have to inspect what's actually getting sent back, not sure off hand the structure of the data that's returned... but that'll be close.

FareedR's avatar

thank you for your reply . i've tried your code, the file is downloaded . but just a plain file . not a return from browsershot.

willvincent's avatar

just a plain file

Not sure what that means... as I said you'll probably have to examine the response to be sure you're handling it properly, I wouldn't necessarily expect my example to work without modifications.

Have you verified that the pdf is properly generated with the expected contents? Is it possible that the pdf generation process takes longer than the time to get the response somehow?

Might be better to simple return a link, and instead of the downloadFile method I shared expecting a data blob, populate the provided link from the response into the generated anchor tag.

FareedR's avatar
FareedR
OP
Best Answer
Level 3

after make some changes . now it works. thank you for your time

// controller
	$data = [
            'name' => "stock_".$from.'_'.$to.'.pdf',
            'file' => \Storage::disk('public')->url("stock_".$from.'_'.$to.'.pdf')
        ];
        return $data;
// blade js
				var fileName = data.name;
                                const a = document.createElement('a');
                                a.style.display = 'none';
                                a.href = data.file;
                                a.download = fileName;
                                document.body.appendChild(a);
                                a.click();
jeremy's avatar

Old thread, but I came across it in my search for a solution. Here is what I did:

    Browsershot::url($urlSource)->newHeadless()->savePdf($pdfName);
    return response()->download($pdfName)->deleteFileAfterSend(true);

You don't have to use newHeadless() for this to work. Works great in all configurations.

2 likes

Please or to participate in this conversation.