1

I have a laravel-application where I want to generate a PDF from the values, the user has entered in some input fields. So when the user has entered the data, he clicks a button which generates the PDF and downloads it immediately afterwards automatically. All this should happen on the same route/view. The PDF should not be stored somewhere.

So right now, when I click the button, the entered Data gets through, e.g. stored in the DB, and it seems that a PDF is created, but I can't see or find it, and my browser does not inform me that there is a PDF available for download.

Before I started, I installed the laravel-dompdf-plugin, and followed the instructions.

So my route look like this

Route::view('formpage', 'app.statement')->name('statement'); // The blade view with the Form

Route::post('statement', 'MyController@generatePDF')->name('generatePDF'); // this is where I post the form

This is my controller

use PDF;

class MyController extends Controller {

  public function generatePDF(Request $request){

    $statement = Statement::create([
        'name' => $validated['name'],
        'email' => $validated['email'],
        'phone' => $validated['phone'],
        'declaration_date' => $validated['declaration_date'],
    ]);

    $pdf = PDF::loadView('pdf.statement', $statement);

    return $pdf->download('File__'.$statement->name.'.pdf');

  }

}

I posting the form with javascript by using axios by simply doing this:

$('#submitBtn').click(function(e) {
  const formData = new FormData();

  formData.append(
     "name",
     $("#statement")
        .find('input[name="name"]')
        .val()
   );

   ...etc with all other fields

  axios.post($("#statement form").attr("action"), formData)
            .then(response => {
               $('#submitBtn')
                    .attr("disabled", "disabled")
                    .addClass("disabled")
                    .html('<i class="fas fa-fw fa-check"></i> Success'); */
                $("#statement form")[0].reset();
            })
            .catch(error => {
                console.log("ERR: ", error); // DEBUG

                $("#statement .text-danger").show();

                $('#sworn-statement button[type="submit"]')
                    .removeAttr("disabled")
                    .removeClass("disabled")
                    .html("Send");
            });

}

What am I doing wrong?

UPDATE

I tried to do this:

const FileDownload = require("js-file-download");

axios.post($("#statement form").attr("action"), formData)
     .then(response => {
          FileDownload(response.data,"File.pdf");
     }).catch(error => {
         console.log('error:', error); 
     });

which gives me a blank page.

ST80
  • 3,565
  • 16
  • 64
  • 124
  • You are posting the request with javascript to the `statement` route? If it's so you need to post your javascript code which is essential for this problem. It's working if you navigate your browser onto the page because he gets the content-type pdf as response and knows how to handle it. If you posting your stuff async with js the browser doesn't have any idea whats happening because he just get an response object with the pdf in it and if you don't handle it there it will be ignored. – thmspl Jan 08 '20 at 12:03
  • @thmspl I've updated my question - added the js code – ST80 Jan 08 '20 at 12:09
  • since you're creating the request via javascript, the result also comes through javascript. So the download likely is not being triggered because it comes back as an AJAX response. You likely need to convert the incoming data into a file and trigger the download via JS, here's an example how https://blog.jayway.com/2017/07/13/open-pdf-downloaded-api-javascript/ . The response might not be in blob format so watch out for that too. – Cârnăciov Jan 08 '20 at 12:13

1 Answers1

1

So as I said in the comments your problem is that the file is in the response you get from the axios POST request. If you don't handle the filedownload after you get the response nothing will happen.

You can use the js-file-download module. After you've installed this module you can modify your code to something like this:

const FileDownload = require('js-file-download');

axios.get(YOUR_URL)
   .then((response) => {
        FileDownload(response.data, YOUR_FILE_NAME);
   });

There's also an another solution with JQuery which I got from that answer:

$.ajax({
    type: "POST",
    url: url,
    data: params,
    success: function(response, status, xhr) {
        // check for a filename
        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        }

        var type = xhr.getResponseHeader('Content-Type');
        var blob = new Blob([response], { type: type });

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') {
                    window.location = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
});

Just replace the url and params attributes with your stuff. This creates also a POST request and handles the incomming PDF file as filedownload after the response arrives.

thmspl
  • 2,437
  • 3
  • 22
  • 48
  • The first method, gives me a blank page... :-/ – ST80 Jan 08 '20 at 12:29
  • Errors in the console? – thmspl Jan 08 '20 at 12:31
  • mmm no... but `response.data` is kind of some gibberish... does the controller return something wrong maybe? – ST80 Jan 08 '20 at 12:32
  • Did you use the jquery or the axios code? The "gibberish" stuff is your PDF as binary. So no worries about that. – thmspl Jan 08 '20 at 12:37
  • Please update your question with the code you are currently using. – thmspl Jan 08 '20 at 12:39
  • Is your code inside of the `$('#submitBtn').click(function(e) {}` function? – thmspl Jan 08 '20 at 12:46
  • The page is changing? It's javascript the page shouldn't change. – thmspl Jan 08 '20 at 13:00
  • ah no, what I mean is - when I click the submit button of my form, it generates and downloads a pdf file automatically, but when i open it, its a blank PDF file :-) – ST80 Jan 08 '20 at 13:03
  • Ah cool. So I think something with your PDF isn't good. Replace `->download` with `->stream` and navigate to the route with your browser. Now you should be able to see what will be generated. – thmspl Jan 08 '20 at 13:11
  • `navigate to the route with your browser` - could you maybe explain? – ST80 Jan 08 '20 at 13:21
  • Change `Route::post('statement',` to `Route::get('statement',` and then enter the url in your browser window like `YOUR_HOST/statement`. – thmspl Jan 08 '20 at 13:25