Download file through an ajax call php
Asked Answered
R

6

40

I have a button and onclick it will call an ajax function.

Here is my ajax function

function csv(){

    ajaxRequest = ajax();//ajax() is function that has all the XML HTTP Requests

    postdata = "data=" + document.getElementById("id").value;

    ajaxRequest.onreadystatechange = function(){
        var ajaxDisplay = document.getElementById('ajaxDiv');
        if(ajaxRequest.readyState == 4 && ajaxRequest.status==200){
            ajaxDisplay.innerHTML = ajaxRequest.responseText;           
        }
    }

    ajaxRequest.open("POST","csv.php",false);
    ajaxRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    ajaxRequest.send(postdata);
}

I create the csv file based on the user input. After it's created I want it to prompt download or force download(preferably force). I am using the following script at the end of the php file to download the file. If I run this script in a separate file it works fine.

$fileName = 'file.csv';
$downloadFileName = 'newfile.csv';

if (file_exists($fileName)) {
    header('Content-Description: File Transfer');
    header('Content-Type: text/csv');
    header('Content-Disposition: attachment; filename='.$downloadFileName);
    ob_clean();
    flush();
    readfile($fileName);
    exit;
}
echo "done";

But If I run it at the end of csv.php it outputs the contents of the file.csv into the page(into the ajaxDiv) instead of downloading.

Is there a way to force download the file at the end of csv.php?

Robichaux answered 12/7, 2011 at 17:55 Comment(0)
M
45

AJAX isn't for downloading files. Pop up a new window with the download link as its address, or do document.location = ....

Mcgaw answered 12/7, 2011 at 17:57 Comment(3)
I believe technically it's better to set window.location; see this discussion: #7858378Joist
Why is AJAX not for downloading files?Lacrosse
I can't set window.location because I need to set some special headers for my requestEumenides
E
21

A very simple solution using jQuery:

on the client side:

$('.act_download_statement').click(function(e){
    e.preventDefault();
    form = $('#my_form');
    form.submit();
});

and on the server side, make sure you send back the correct Content-Type header, so the browser will know its an attachment and the download will begin.

Eldoree answered 10/11, 2012 at 14:22 Comment(4)
+1 This worked great for me for POSTing data to the server and getting a ZIP file download as the response. No page reload.Creak
Can you give a little more detail and provide some code on what happens on both ends?Malemute
code is nice but what happens error occurs it is redirected a error page which is not requiredSol
What if i want to show some loader for time being business logics are implementing and creating file content.Agra
R
15

@joe : Many thanks, this was a good heads up!

I had a slightly harder problem: 1. sending an AJAX request with POST data, for the server to produce a ZIP file 2. getting a response back 3. download the ZIP file

So that's how I did it (using JQuery to handle the AJAX request):

  1. Initial post request:

    var parameters = {
         pid     : "mypid",
       "files[]": ["file1.jpg","file2.jpg","file3.jpg"]
    }
    
    

    var options = { url: "request/url",//replace with your request url type: "POST",//replace with your request type data: parameters,//see above context: document.body,//replace with your contex success: function(data){ if (data) { if (data.path) { //Create an hidden iframe, with the 'src' attribute set to the created ZIP file. var dlif = $('<iframe/>',{'src':data.path}).hide(); //Append the iFrame to the context this.append(dlif); } else if (data.error) { alert(data.error); } else { alert('Something went wrong'); } } } }; $.ajax(options);

The "request/url" handles the zip creation (off topic, so I wont post the full code) and returns the following JSON object. Something like:

 //Code to create the zip file
 //......
 //Id of the file
 $zipid = "myzipfile.zip"
 //Download Link - it can be prettier
 $dlink = 'http://'.$_SERVER["SERVER_NAME"].'/request/download&file='.$zipid;
 //JSON response to be handled on the client side
 $result = '{"success":1,"path":"'.$dlink.'","error":null}';
 header('Content-type: application/json;');
 echo $result;

The "request/download" can perform some security checks, if needed, and generate the file transfer:

$fn = $_GET['file'];
if ($fn) {
  //Perform security checks
  //.....check user session/role/whatever
  $result = $_SERVER['DOCUMENT_ROOT'].'/path/to/file/'.$fn;
  if (file_exists($result)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/force-download');
    header('Content-Disposition: attachment; filename='.basename($result));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: ' . filesize($result));
    ob_clean();
    flush();
    readfile($result);
    @unlink($result);
  }

}
Rumpus answered 12/7, 2012 at 13:30 Comment(1)
Just the & needed changing to a ? for the query string. Thanks for the awesome help with this though! - I tried to do an edit but it was such a small one it wouldn't let me!! $dlink = 'http://'.$_SERVER["SERVER_NAME"].'/request/download?file='.$zipid;Add
L
9

I have accomplished this with a hidden iframe. I use perl, not php, so will just give concept, not code solution.

Client sends Ajax request to server, causing the file content to be generated. This is saved as a temp file on the server, and the filename is returned to the client.

Client (javascript) receives filename, and sets the iframe src to some url that will deliver the file, like:

$('iframe_dl').src="/app?download=1&filename=" + the_filename

Server slurps the file, unlinks it, and sends the stream to the client, with these headers:

Content-Type:'application/force-download'
Content-Disposition:'attachment; filename=the_filename'

Works like a charm.

Liverpool answered 25/3, 2012 at 8:21 Comment(2)
is "slurps" a technical term?Nw
@ZachSmith, yes. Slurping is the act of reading an entire file before processing. I was describing my own process, and the files are all of known size, well under 1mb. If file size was unknowable, or very large, then a streaming process of some sort would be better.Liverpool
P
6

You can't download the file directly via ajax.

You can put a link on the page with the URL to your file (returned from the ajax call) or another way is to use a hidden iframe and set the URL of the source of that iframe dynamically. This way you can download the file without refreshing the page.

Here is the code

$.ajax({
    url : "yourURL.php",
    type : "GET",
    success : function(data) {
        $("#iframeID").attr('src', 'downloadFileURL');
    }
});
Phallic answered 17/3, 2014 at 13:22 Comment(2)
Posting relevant code would help illustrate what you're trying to say here.Mormon
on your ajax success response you can set the hidden iframe srcPhallic
J
1

You can do it this way:

On your PHP REST api: (Backend)

header('Content-Description:File Transfer');
header('Content-Type:application/octet-stream');
header('Content-Disposition:attachment; filename=' . $toBeDownloaded);
header('Content-Transfer-Encoding:binary');
header('Expires:0');
header('Cache-Control:must-revalidate');
header('Pragma:public');
header('Content-Length:'.filesize($toBeDownloaded));
readfile($toBeDownloaded);
exit;

On your javascript code: (FRONTEND)

const REQUEST = `API_PATH`;
try {
  
  const response = await fetch(REQUEST, {
    method: 'GET',
  })

  const fileUploaded = await response.blob();
  const url = window.URL.createObjectURL(fileUploaded);
  const link = document.createElement('a');
  link.href = url;
  link.setAttribute('download', 'YOUR_FILE_NAME_WITH_EXTENSION');
  document.body.appendChild(link);
  link.click();
} catch (error) {
  console.log(error)
}
Josephinajosephine answered 9/10, 2022 at 22:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.