ajax php file download and update mysql database field

Posted on 2009-04-22
Last Modified: 2013-12-12
I am starting to get to grips with ajax and need some assistance to code for form submission in which a mysql field value is updated and a file is downloaded. The principle of the routine is to checkout/download a file for revision and, having done so, ensure that nobody else can checkout the same file.

The first part of the problem I have done by simply using a standard php update function when the form is submitted. However, I am trying to use ajax to manage file download and I am having difficulty getting it configured.

I have attached a screenshot of the form page which is a list of radio buttons and a 'Checkout' (submit) button. When one of the radio buttons is selected and the button clicked, the database entry is updated to set the docCheckedOut field in the document table to true. At the same time a file download dialog should appear, and this isn't happening.

The pertinent parts of the code are shown below. All assistance gratefully received!
<!-- ajax functions all contained in lib.js-->

<!-- Process the download -->

function downloadFile(fileName)


	// Takes the file information and passes it to downloadFile.php

	xmlhttp = getxmlhttpObject();

	if (xmlhttp == null)


		alert ("Browser does not support HTTP Request");



	var serverPage = "../includes/downloadFile.php";

	obj = document.getElementById(fileName);

	processAjax(serverPage, obj, "post", "fileName");



<!-- process the ajax request -->

function processAjax(serverPage, obj, getOrPost, str)


	// Get an xmlhttpRequest object

	xmlhttp = getxmlhttpObject();

	if (getOrPost == "get")

	{"GET", serverPage);

		xmlhttp.onreadystatechange = function()


			if (xmlhttp.readyState == 4 && xmlhttp.status == 200)

				obj.innerHTML = xmlhttp.responseText;





	{"POST", serverPage, true)

		xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");

		xmlhttp.onreadystatechange = function()


			if (xmlhttp.readyState == 4 && xmlhttp.status == 200)

				obj.innerHTML = xmlhttp.responseText;






<!-- get the xmlhttp object -->

function getxmlhttpObject()


	// Create a boolean to check for a valid MS IE browser instance

	var xmlhttp = false;


	// Check if IE is being used



		// If the js version is > 5

		xmlhttp=new ActiveXObject("Msxml2.XMLHTTP");


	catch (e)




			// If js version <= 5, then use the older activeX object

			xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");




			// Otherwise a non-IE browser is being used

			xmlhttp = false;




	// If not using IE, create a js instance of the object

	if (!xmlhttp && typeof XMLHttpRequest != 'undefined')


		xmlhttp = new XMLHttpRequest();



	return xmlhttp;



<!-- php file download routine (courtesy of Zubrag) -->


// downloadFile.php

// Download a file (with download resumption capability)


// Define the downloadable directory




This function takes a path to a file to output ($file), 

the filename that the browser will see ($name) and 

the MIME type of the file ($mime_type, optional).


If you want to do something on download abort/finish,



if(!is_readable($file)) die('File not found or inaccessible!');


$size = filesize($file);

$name = rawurldecode($name);


/* Figure out the MIME type (if not specified) */

$known_mime_types=array("pdf" => "application/pdf",

			"txt" => "text/plain",

			"html" => "text/html",

			"htm" => "text/html",

			"exe" => "application/octet-stream",

			"zip" => "application/zip",

			"doc" => "application/msword",

			"xls" => "application/",

			"ppt" => "application/",

			"gif" => "image/gif",

			"png" => "image/png",

			"jpeg"=> "image/jpg",

			"jpg" =>  "image/jpg",

			"php" => "text/plain");


if ($mime_type=='')


$file_extension = strtolower(substr(strrchr($file,"."),1));

if(array_key_exists($file_extension, $known_mime_types)) $mime_type=$known_mime_types[$file_extension];

else $mime_type="application/force-download";



@ob_end_clean(); //turn off output buffering to decrease cpu usage


// required for IE, otherwise Content-Disposition may be ignored

if(ini_get('zlib.output_compression')) ini_set('zlib.output_compression', 'Off');


header('Content-Type: ' . $mime_type);

header('Content-Disposition: attachment; filename="'.$name.'"');

header("Content-Transfer-Encoding: binary");

header('Accept-Ranges: bytes');


/* The three lines below basically make the 

download non-cacheable */

header("Cache-control: private");

header('Pragma: private');

header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");


// multipart-download and download resuming support



list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2);

list($range) = explode(",",$range,2);

list($range, $range_end) = explode("-", $range);


if(!$range_end) $range_end=$size-1;

else $range_end=intval($range_end);

$new_length = $range_end-$range+1;

header("HTTP/1.1 206 Partial Content");

header("Content-Length: $new_length");

header("Content-Range: bytes $range-$range_end/$size");





header("Content-Length: ".$size);



/* output the file itself */

$chunksize = 1*(1024*1024); //you may want to change this

$bytes_send = 0;

if ($file = fopen($file, 'r'))


if(isset($_SERVER['HTTP_RANGE'])) fseek($file, $range);

while(!feof($file) && (!connection_aborted()) && ($bytes_send<$new_length))


$buffer = fread($file, $chunksize);

print($buffer); //echo($buffer); // is also possible


$bytes_send += strlen($buffer);




else die('Error - can not open file.');



Open in new window

Question by:kcalder

    Author Comment

    Oops, forgot to post the code for the form on the calling's below.
    <!-- this is an excerpt from the calling page -->
    <!-- lib.js is stated as an included file in the head -->
    <form action="<?php echo $editFormAction; ?>" method="post" name="checkoutDoc" id="checkoutDoc">
    					do { ?>
    								// Filename for downloading
    								$fileName = $row_rstDocList['docFilename'].'_'.$row_rstDocList['docRevision'].'.'.$row_rstDocList['docFileType'];
    								// Find the name of the document owner
    								$docOwner = '';
    								// Walk the user array 
    								for ($j = 0; $j < sizeof($user); $j++) if ($user[$j][0] == $row_rstDocList['docOwnerPK']) $docOwner = $user[$j][1];
    								// Display changes according to whether the item has been checked out or not
    								if ($row_rstDocList['docCheckedOut'] == 0)
    									// The document has not been checked out and is available for checking out
    									<td class="revDocListTitle"><input name="checkoutRadio" type="radio" value="<?php echo $row_rstDocList['doc_pk']; ?>" /><?php echo ' '.$row_rstDocList['docTitle']; ?></td>
    									<td class="revDocListRevision"><?php echo $row_rstDocList['docRevision']; ?></td>
    									<td class="revDocListApplication"><?php echo $row_rstDocList['docApplication']; ?></td>
    									<td class="revDocListOwner"><?php echo $docOwner; ?></td>
    									<td class="revDocListZone"><?php echo $row_rstDocList['zoneTitle']; ?></td>
    									<td class="revDocListPassword"><?php echo $row_rstDocList['agPassword']; ?></td>
    									<input type="hidden" name="checkedOut" id="checkedOut" value="1" />
    									<input type="hidden" name="checkedOutBy" id="checkedOutBy" value="<?php echo $_SESSION['MM_Username']; ?>" />
    									<input type="hidden" name="theFile" id="theFile" value="<?php echo $fileName; ?>" />
    									// Current reviewer is always initially the person who has checked the document out 
    									$currentReviewer = $_SESSION['MM_Username'];
    									<input type="hidden" name="docReviewer" id="docReviewer" value="<?php echo $currentReviewer; ?>" />
    								<?php }
    									// The document has been checked out so find the name of the person who checked it out
    									$checkedOutBy = '';
    									// Walk the array 
    									for ($k = 0; $k < sizeof($user); $k++) if ($user[$k][0] == $row_rstDocList['docCheckedOutBy']) $checkedOutBy = $user[$k][1];
    										<td class="revDocListTitleCheckedOut">
    											<input name="checkoutDoc" type="radio" value="<?php echo $row_rstDocList['doc_pk']; ?>" disabled="disabled">
    												<a class="menuCheckedOut" href="#" title="<?php echo $fileName.' has been checked out for revision by '.$checkedOutBy; ?>"><?php echo $row_rstDocList['docTitle']; ?>
    										<td class="revDocListRevisionCheckedOut"><?php echo $row_rstDocList['docRevision']; ?></td>
    										<td class="revDocListApplicationCheckedOut"><?php echo $row_rstDocList['docApplication']; ?></td>
    										<td class="revDocListOwnerCheckedOut"><?php echo $docOwner; ?></td>
    										<td class="revDocListZoneCheckedOut"><?php echo $row_rstDocList['zoneTitle']; ?></td>
    										<td class="revDocListPasswordCheckedOut"><?php echo $row_rstDocList['agPassword']; ?></td>											
    								<?php } ?>
    					<?php } while ($row_rstDocList = mysql_fetch_assoc($rstDocList)); ?>
    								<?php // TO DO: Write an ajax onclick function for the submit button that downloads the file ?>								
    								<td class="revDocListTitle" colspan="6"><input name="submit" id="submit" type="submit" 
    																			value="Checkout" onclick="downloadFile(document.getElementById(theFile))" />
    					<input type="hidden" name="MM_update" value="checkoutDoc" />

    Open in new window

    LVL 40

    Expert Comment

    I've just had to learn about AJAX and file downloading.

    Here is what I found.

    When a user selects a file download (non AJAX, normal file browsing), the browser generates a request, the server responds with appropriate headers which tell the browser that the data that follows is for the user and not to be automatically handled by the browser.

    When AJAX makes the request, it is AJAX that gets the response. AJAX does NOT invoke a file save dialogue or anything like that.

    The solution I had was to create a hidden iframe whose URL was the downloader. The iframe is part of the browser, so when it gets the headers saying this is a file download, the browser would open the file save dialogue.

    LVL 40

    Accepted Solution

    I had a rather complicated Crystal Report to run server side which outputs a PDF, MS Word document or MS Excel spreadsheet.

    I use an AJAX request to start the Crystal Report run.
    The response of the request is the URL to be used in the iframe so that it can download the saved file.
    The file is deleted once it has been sent (all part of the iframe request handler).

    I use Prototype for my JS needs (and I extend it where I need more functionality).

    Below is an edited version of the code I use.

    The AJAX part simply returns a JS like which calls the tabRG_GenerateReport() function with JSON'd data.

    I hope this helps. This solution is working for our sites very well. It has enough protection to stop file downloading directly, as the files don't exist once the download is complete. The files are only created when the AJAX request comes in. All quite tightly controlled.

    If not, let me know what else you need.

    function tabRG_GenerateReport(o_Gen)
    	var o_Date = new Date(); // Make sure all requests are unique
    	var o_FrameRepGen = new Element
    			className : 'reportDownloadFrame',
    			id        : 'download_' + o_Date.getTime(),
    			src       : o_Gen.URL

    Open in new window


    Author Closing Comment

    I am closing this question out because I have decided to take a different approach using jQuery. I am still having difficulty making that work but it seems appropriate to develop a separate question. Thanks for your assistance.

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    What Is Threat Intelligence?

    Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

    Creating and Managing Databases with phpMyAdmin in cPanel.
    JavaScript can be used in a browser to change parts of a webpage dynamically. It begins with the following pattern: If condition W is true, do thing X to target Y after event Z. Below are some tips and tricks to help you get started with JavaScript …
    This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.
    The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…

    779 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    9 Experts available now in Live!

    Get 1:1 Help Now