Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trigger download dialog right after headers received

I have a PDF export that takes a while to create the PDF. I want the user to be able to click the export link and be presented with a download dialog right away. This way they can start the download and just wait for it to complete. Instead of clicking the link, wait for the generation and then wait for the download again.

Here's a very simple example in PHP:

<?php

header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename=test.pdf');
flush();

// time consuming PDF creation happens here
sleep(15);
echo 'pdf contents would be here';

The idea is to send the appropriate headers, flush() them to the browser, slowly create the PDF and finally send it to the browser.

This works perfectly in chrome. The Download Dialog pops up immeadiately and the sleep is part of the download waiting time.

In Firefox and InternetExplorer this does not work. Those browsers wait the full 15 seconds before showing the download dialog.

Any idea how to make the download dialog pop up immeadiately would be greatly appreciated.

like image 512
Andreas Gohr Avatar asked Nov 23 '17 11:11

Andreas Gohr


1 Answers

Some browsers wait for the actual content before showing the download dialog. So, the solution is simple: send some PDF content before creating the PDF content.

Wait a minute. How do you send something before you have it? That sounds like it would require a time machine, right? Don't worry, there is a way to get around it.

PDF content starts with %PDF, thus you simply have to send %PDF before calling flush(). After the PDF creation completes, remove the first 4 bytes of the newly created PDF content before echoing it.

<?php
// disable output buffering
while (@ob_end_clean());

header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename=test.pdf');

// send some content to trigger the download dialog
echo '%PDF';
flush();

// time consuming PDF creation happens here
sleep(15);
$pdfContent = '%PDF-pdf contents would be here';
echo substr($pdfContent, 4);

This solution works in any language because it doesn't rely on any PHP-only feature.

If you want to do the absolute minimum, you can also send 1 byte % and then remove just the first byte before echoing. Same result.

like image 114
Rei Avatar answered Oct 21 '22 11:10

Rei