2

I am trying to use PHP to force a download on a client computer (with the file dialog- nothing sinister). I have found many pages that recommend I use the header() function to control the response from my PHP script, but I am having no luck with this. The code I have is as follows:

$file = $_POST['fname'];

if(!($baseDir . '\\AgcommandPortal\\agcommand\\php\\utils\\ISOxml\\' . $file)) {
    die('File not found.');
} else {
    header('Pragma: public');
    header('Content-disposition: attachment; filename="tasks.zip"');
    header('Content-type: application/force-download');
    header('Content-Length: ' . filesize($file));
    header('Content-Description: File Transfer');
    header('Content-Transfer-Encoding: binary');
    header('Connection: close');
    ob_end_clean();
    readfile($baseDir . '\\AgcommandPortal\\agcommand\\php\\utils\\ISOxml\\' . $file);
}

I am calling it using this JavaScript:

        $.ajax({
            url: url,
            success: function(text) {
                var req = new XMLHttpRequest();
                req.open("POST", 'php/utils/getXMLfile.php', true);
                req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                req.send('fname=' + encodeURIComponent(text));
            }
        });

This returns the contents of the file as text, but does not trigger a download dialog. Does anyone have any suggestions?

Crash
  • 219
  • 6
  • 16
  • I would not consider it a duplicate. Here the problem is slightly different. The issue is that the result of a post action does not trigger the download behaviour specified in the header of the answer generated by the php. – ALoopingIcon Feb 11 '17 at 16:37

4 Answers4

6

Instead of using AJAX, just redirect the browser to the relevant URL. When it receives the content-disposition:attachment header, it will download the file.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • If I redirect the browser, won't it clear out the page that is already loaded? This is somewhat problematic, as this is just a small part of a much larger page. – Crash Aug 06 '12 at 21:52
  • +1 Very true, why would you use AJAX if you don't need the result of the call? – Ruan Mendes Aug 06 '12 at 21:52
  • @Crash it won't redirect, just "save file" dialog will popup... – Dejan Marjanović Aug 06 '12 at 21:54
  • I do need the result- the situation is difficult. I am calling a PHP script that calls an ASP script (multiple programmers using multiple languages and strange interactions), then returns the name of the file generated by the ASP. The filename is then sent to the above PHP code, where it is meant to be downloaded. Though now that I think about it, I could probably do this without a second call- but the problem of getting the download to work is still an issue. – Crash Aug 06 '12 at 21:55
  • @Crash You don't need this AJAX call though. You should just be able to do `location.href= ''` and as long as you have the right headers for a download, the current page won't be unloaded, but the save as dialog will be displayed – Ruan Mendes Aug 06 '12 at 21:57
  • Ok, I think this is going to work for me. Quitting time for today, but it is looking promising. I'll test it a little better tomorrow. Thanks! – Crash Aug 06 '12 at 22:04
  • 3
    ...and fix the security hole by replacing `$file = $_POST['fname'];` with `$file = basename($_POST['fname']);` – symcbean Aug 06 '12 at 22:13
  • @symcbean thanks for the tip- hadn't thought about that. – Crash Aug 07 '12 at 14:04
  • @Kolink this seems to be working well, so I will award the answer. However, as I want to use a POST request, I am using a slightly different method. I will include the code below. – Crash Aug 07 '12 at 14:06
1

Few suggestions:

1.

if(!($baseDir . '\\AgcommandPortal\\agcommand\\php\\utils\\ISOxml\\' . $file)) {

Instead:

if(!file_exists($baseDir ....)){

2.Don't need size.

3.Try this one:

 header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($fullpath));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    ob_clean();
    flush();
    readfile($fullpath);
    exit;
Ofir Baruch
  • 10,323
  • 2
  • 26
  • 39
0

I would try sending a header from PHP like this, to replace your application/force-download header:

header("Content-type: application/octet-stream");
Oliver Spryn
  • 16,871
  • 33
  • 101
  • 195
0

Kolink's answer worked for me (change the window location to the php file), but as I want to send POST variables along with the request, I ended up using a hidden form instead. The code I am using is as follows:

                var url = 'php/utils/getXMLfile.php';
                var form = $('<form action="' + url + '" method="post" style="display: none;">' +
                    '<input type="text" name="fname" value="' + text + '" />' +
                    '</form>');
                $('body').append(form);
                $(form).submit();

Thanks for all the answers!

Crash
  • 219
  • 6
  • 16