3

I have a multi-step form in Vue, I am posting the results once I collect all of the information, to a Laravel controller. This is an authenticated area of the site. I am using Passport. So essentially I have a Vue SPA that is the admin area of a website built within the Laravel 5.7 framework.

Vue file:
axios.post('/api/quotes/finalize', this.wizardModel)
                        .then(response => {
                            if (response.data.success) {
                                //
                            }
                        })
                        .catch(err => {
                            if (err.response.status == 401) {
                                window.location.href = '/login';
                            }
                            swal.showValidationError(
                                `Request failed: ${error}`
                            )

                        })

The controller gets data and makes a pdf. All of that is working. It then has three actions to look at - email the PDF, email the PDF with CC, and download the PDF.

    public function finalizeQuote(Request $request)
{
        $data = $request->all();
        $data['phone'] = $this->formatTelephone($data['phone']);

        $dateStamp = date('Ymdhis', strtotime('now'));
        $fileName = 'quote-' . $dateStamp . '.pdf';
        $html = View::make('quotes.quote', compact('data'))->render();

        $conv = new Converter();
        $conv->addPage($html)
            ->save('storage/' . $fileName);

        if ($data['actions']['emailPDF']) {
                $message = (new Proposal('storage/' . $fileName))
                                ->onConnection('redis')
                                ->onQueue('emails');

                if ($data['actions']['carbonCopy']) {
                    Mail::to($data['emailAddress'])
                        ->cc(Auth::user()->email)
                        ->queue($message);
                } else {
                    Mail::to($data['emailAddress'])->queue($message);
                }

            }

            if ($data['actions']['downloadPDF']) {
                return response()->download(public_path('storage/' . $fileName));
            }
}

So in dev tools I see the pdf file in the response. No download occurs in the browser. I am sure I am just missing something fundamental here.

Larry Ball
  • 53
  • 1
  • 7
  • What is the content type in the response header of the download request? – pidizzle Sep 28 '18 at 16:33
  • application/pdf is the content type under the "Response Headers" section in Chrome dev tools. Under the "Request Headers" it is application/json;charset-UTF-8. – Larry Ball Sep 28 '18 at 16:37
  • Ok is there a `Content-Disposition` header present in the response headers? If not, add `Content-Disposition: attachment; filename="filename.pdf"` to the response header. If the Content-Disposition header exists, let me know what the value of it is. – pidizzle Sep 28 '18 at 16:40
  • content-disposition: attachment; filename=quote-20180928035845.pdf is what already is there; that is the correct filename – Larry Ball Sep 28 '18 at 16:52
  • Interesting...and you are sure that there is a file at that path you are returning? Also what is the value of $data['actions']? – pidizzle Sep 28 '18 at 17:35
  • Yes the file is there and the $data['actions']['downloadPDF'] is true. The pdf source is in the response, but nothing happens. If I pull down the pdf from the server it views fine, looks good. – Larry Ball Sep 28 '18 at 17:39
  • Oh yeah totally forgot. XHR requests will not invoke the download (axios uses XHR under the hood). Check out this link https://stackoverflow.com/questions/26737883/content-dispositionattachment-not-triggering-download-dialog – pidizzle Sep 28 '18 at 17:56
  • Does this answer your question? [Force download GET request using axios](https://stackoverflow.com/questions/43432892/force-download-get-request-using-axios) – miken32 Jun 30 '21 at 19:31

2 Answers2

7

Ajax requests alone cannot invoke a download. You could have a hidden form that posts your data to your api endpoint, probably the quickest solution.

Axios has a built in way to do this easily

axios({
    method: 'post',
    url: '/api/quotes/finalize',
    data: wizardModelFormData,
    config: { headers: {'Content-Type': 'multipart/form-data' }}
    })

Instead of sending json in the request, you would have to send form data. It is pretty easy to setup a form data object, and a quick google on FormData should be enough.

Another way is to do something like this. Have the endpoint return a download url for your file,

   if ($data['actions']['downloadPDF']) {
        return response()->json(["download_url" => "/api/download/" . $fileName]);
    }

and on the client side, use window.location to set the browser location to this api endpoint that only returns files (in this case /api/download/{fileName}). The browser would then download any file returned from that location you set (if you have the headers setup correctly).

This would mean you would have to implement the /api/download/{fileName} route, but that is where you would put something like this.

return response()->download(public_path('storage/' . $fileName));

Obviously that is just a simple explanation and it doesn't need to be implemented exactly the same way, but it gets the idea across.

Now you would want to make sure that you don't put any sensitive files in storage/ and have some permissions setup.

pidizzle
  • 720
  • 6
  • 12
  • This has been a very useful answer too. I wish I could accept both, I have ended up using principles from both. Thank you very much. – Larry Ball Sep 28 '18 at 20:15
  • @LarryBall Haha no worries, glad it helped. You could always upvote my answer! – pidizzle Sep 28 '18 at 20:17
3

Axios is used for XHR requests. Typically, downloading files is done through normal GET requests but here you have an XHR post request to download files.

To download files over AJAX, some folks use libraries and some create a blob link from the response, append it to DOM and then, trigger a click on the link.

You can refer this gist and SO post for further details.

Personally, I prefer simple non-XHR get requests for file downloads.

Paras
  • 9,258
  • 31
  • 55
  • I am posting the entire form's data. Within the controller I am creating a pdf, and depending on the selections by the user, either emailing it or letting them download it. I am not saying you are not correct, maybe what I am trying to do is not possible. – Larry Ball Sep 28 '18 at 17:55
  • I'm not saying you cant download files using XHR post requests. If you refer the gist and SO post, you can do so – Paras Sep 28 '18 at 18:01
  • Got it. Thank you very much. – Larry Ball Sep 28 '18 at 18:15