ducin
ducin

Reputation: 26437

Downloading file though AJAX POST

I'm trying to provide a simple download using AJAX POST request. A user clicks a <span> and the download begins (or a download dialog shows up, depending on the browser). I've implemented all this, but the AJAX response doesn't trigger any browser download behavior and I don't know why.

Here's my code:

HTML/twig:

<span id="export_csv">csv - click me to download the file</span>

javascript:

<script type="text/javascript">
$(document).ready(function() {
  $('#export_csv').click(function(){
    var params = "some params";
    $.ajax({
      type: 'POST',
      url:  "{{path('ExportBundle_export', {'format': 'csv','report' : 'basic'})}}",
      data: params
    });
  });
});
</script>

Controller code (symfony2, but it doesn't matter here):

$response = new Response(file_get_contents($document->getPath()));
$response->setStatusCode(200);
$response->headers->set('Content-Type', $document->getMime());
$response->headers->set('Content-Description', 'Submissions Export');
$response->headers->set('Content-Disposition', 'attachment; filename='
    . $filename . '.' . $format);
$response->headers->set('Content-Transfer-Encoding', 'binary');
$response->headers->set('Pragma', 'no-cache');
$response->headers->set('Expires', '0');
return $response;

This is example request:

Request URL:http://myapp.local/app_dev.php/export/basic/csv
Request Method:POST
Status Code:200 OK

and response headers:

cache-control:private, must-revalidate
Connection:close
content-description:Submissions Export
content-disposition:attachment; filename=report_1363013244.csv
Content-Length:790
content-transfer-encoding:binary
Content-Type:text/csv; charset=UTF-8
Date:Mon, 11 Mar 2013 14:47:24 GMT
expires:0
pragma:no-cache
Server:Apache/2.2.22 (Ubuntu)
x-debug-token:513dee7c99fdb
X-Powered-By:PHP/5.3.10-1ubuntu3.5

I've checked Network tab in Google Chrome firebug-alike toolbar and the response looks exactly like the file. Maybe there is something wrong with my headers, but I checked them twice already...

PS 1

I'd like to make it using AJAX (I prefer it more than creating a HTML form).

PS 2

I've got lots of parameters in the client side layer (javascript). I need all of them to be available in the controller (server-side script). When using AJAX, I just pass them in the data key.

Upvotes: 1

Views: 20289

Answers (3)

Aris
Aris

Reputation: 5057

I don't think its possible using ajax, I had the same issue and the download was not triggered. My solution was using only PHP:

  1. Create a new file when the page is loaded, on the background. This will be stored in a server location.

    $out = fopen('filename', 'w+');
    //write data in file
    fputcsv($out, some csv values );
    
  2. have a link to the client to download it

    <a href='<?php echo $filename;?>' >Export Data to CSV file</a>
    

And depending on how often this is done, you must clean-up the downloads regularly to not fill the server.

Upvotes: 0

Max Doumit
Max Doumit

Reputation: 1115

I do not get why you want to trigger an ajax call for download? Why don't you just do a regular <a href="download/me">Download</a> and set the mime type of that page. This way you can run any php code and then echo a mime type, this will not cause the page to reload and you will stay in the page from where you did the click.

http://davidwalsh.name/php-header-mime

Upvotes: 4

Alex
Alex

Reputation: 2146

You don't download the file using AJAX.

You just have to give the URL like <a href="myFile.jpeg"> but the problem is that you have to force download using headers and URL Rewriting to intercept the requests:

In case of an image:


$filename = basename($_GET['img']); // don't accept other directories
$size = @getimagesize($filename);
$fp = @fopen($filename, "rb");
if ($size && $fp)
{
   header("Content-type: {$size['mime']}");
   header("Content-Length: " . filesize($filename));
   header("Content-Disposition: attachment; filename=$filename");
   header('Content-Transfer-Encoding: binary');
   header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
   fpassthru($fp);
   exit;
}

In this case.. the URL will not be changed (I think this is what you want)

Upvotes: 2

Related Questions