1

I use X-SendFile Apache Module to download large files from the server. Downloads work well. However, when the download starts, I need to output some text to the browser, such as: "Thanks for downloading the file...."

My problem is, that I can not do both in one script. I can either download the file, and then no text is output to the screen. Or, I can output the text to the screen, but then the download script won't start.

How to do both tasks in one PHP script please - downloading a file and sending text content to the browser/webpage? The problem probably originates in HTTP output headers, but I do not know how to solve it.

This it the sample code I use to download files via X-SendFile (works properly on its own):

header('Content-Description: File Transfer');
header("Content-Disposition: attachment; filename=\"". basename($filePath) . "\"");
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
header('Content-Length: ' . filesize($filePath));
header("X-Sendfile: $filePath");

For completion, this is my test echo"" string. If it is placed above the X-Sendfile code, it gets output, but download will not start. If it is put below the X-Sendfile code, the file gets downloaded, but nothign is echoed to the browser:

echo "SUCCESS!!!";
ob_flush();

Constraint:

I need to be able to download the file easily via URL. Example, www.mydomain.com/?fileID=asl523n53twteHLfga. In other words, the PHP download script is within index.php. When the user enters www.mydomain.com, the domain landing page gets displayed. However, when the additional $_GET variable is passed, PHP script recognizes that, and should display Thank you for downloading... instead of the landing page.

A workaround is welcome. Important is to achived the desired result while keeping the above constraint. Tahnk you very much.

Muhammad Hassaan
  • 7,296
  • 6
  • 30
  • 50
Bunkai.Satori
  • 4,698
  • 13
  • 49
  • 77
  • 2
    Not possible. HTTP multi-part isn't supported at all (or supported well) by most browsers. a response normally consists of ONE data stream. most people get around by popping up an intermediate page that has the thankyou, and then a meta-refresh that points at the actual download link. – Marc B Nov 27 '13 at 16:08
  • Marc B, thanks for your answer. I get what you mean. This is ineed doable solution. I will wait to gather some more responses, and if nothing better comes, I will go in the direction you pointed out. Thanks and +1. – Bunkai.Satori Nov 27 '13 at 16:16

2 Answers2

3

How about adding a separate download page that will download the file (i.e. download.php?fileID=XYZ) and having your index page show a message like so:

<?php

function homeScreen() {
  // your home content
}

function downloadFileScreen ($id) {
?>
<h2>Thank you for your download!</h2>
Your download should start automatically, if it does not, click 
<a href="download.php?fileID=<?php print $id; ?>">here</a>
<script type="text/javascript">
window.setTimeout('location.href="download.php?fileID=<?php print $id; ?>"',3000);
</script>
<?php
}

if (isset($_GET['fileID'])) {
  $id= $_GET['fileID'];
  downloadFileScreen ($id);
?>
} else {
  // no ID passed, show home screen
  homeScreen();
}
?>

I haven't heard of any browser support mixing both download and HTTP content display in a single request, but I'll have a look if this can be achieved using ob_start() and ob_flush().

SaschaM78
  • 4,376
  • 4
  • 33
  • 42
  • 1
    Hi Sascha, I have implemented your solution(in a way that works just for me) and it works well. Let me thank you for your answer, and mark it as the accepted answer. Thanks. – Bunkai.Satori Nov 27 '13 at 21:28
  • Thanks a lot, glad it could help you get started! – SaschaM78 Nov 27 '13 at 21:41
3

There is workaround. You can create iframe inside which download the file. See the code for details.

Thank you page: (for example www.mydomain.com/?fileID=asl523n53twteHLfga)

<p>Thank you!</p>
<?php
$fileID = $_REQUEST['fileID'];
// csrf code individual for visitor, so visitor can's send direct link to friend
$csrf = md5(uniqid(rand(), true));
$_SESSION['csrf'] = $csrf;
// with iframe file will be downloaded 'in background'
echo '<iframe src="downloadFile.php?fileID=' . urlencode($fileID) . '&csrf=' . urlencode($csrf) . '" width="0" height="0"></iframe>';
?>

downloadFile.php (for example www.mydomain.com/downloadFile.php?fileID=asl523n53twteHLfga)

<?php
$fileID = $_REQUEST['fileID'];
if ($_REQUEST['csrf'] == $_SESSION['csrf'])
{
    // download file
    header('Content-Description: File Transfer');
    header("Content-Disposition: attachment; filename=\"". basename($filePath) . "\"");
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: no-store, no-cache, must-revalidate');
    header('Pragma: no-cache');
    header('Content-Length: ' . filesize($filePath));
    header("X-Sendfile: $filePath");
    die();
}
else
{
    // csrf from request not equal csrf saved in session, so probably this is direct link to file received from other user
    // redirect to 'thank you' page
    header('Location: /?fileID=' . urlencode($fileID));
    die();
}
?>
Dador
  • 5,277
  • 1
  • 19
  • 23
  • 1
    Hi and thanks for your response. Your answer contains great example, I believe it would work jus perferctly. The solution above from Sascha is another great example. I tried Sascha's solution and it worked well, for me. As I have to make decision, which asnwer will I mark as the accepted answer, I will decide for Sascha, as his response I have actually implemented. I will give you +1 for your great tip. Thank you. – Bunkai.Satori Nov 27 '13 at 21:27