Link to home
Start Free TrialLog in
Avatar of kcalder
kcalderFlag for United Kingdom of Great Britain and Northern Ireland

asked on

ajax php file download and update mysql database field

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");
	 	return;
	}
	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")
	{
		xmlhttp.open("GET", serverPage);
		xmlhttp.onreadystatechange = function()
		{
			if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
				obj.innerHTML = xmlhttp.responseText;
		}
		xmlhttp.send(null);
	}
	else
	{
		xmlhttp.open("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;
		}
		xmlhttp.send(str);
	}
}
 
<!-- 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
	try
	{
		// If the js version is > 5
		xmlhttp=new ActiveXObject("Msxml2.XMLHTTP");
	}
	catch (e)
	{
		try
		{
			// If js version <= 5, then use the older activeX object
			xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
		}
		catch(E)
		{
			// 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) -->
<?php
// downloadFile.php
// Download a file (with download resumption capability)
 
// Define the downloadable directory
define('BASE_DIR','http://MYDOMAIN/docsGeneral/');
 
/*
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,
register_shutdown_function('function_name');
*/
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/vnd.ms-excel",
			"ppt" => "application/vnd.ms-powerpoint",
			"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
if(isset($_SERVER['HTTP_RANGE']))
{
list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2);
list($range) = explode(",",$range,2);
list($range, $range_end) = explode("-", $range);
$range=intval($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");
}
else 
{
$new_length=$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
flush();
$bytes_send += strlen($buffer);
}
fclose($file);
}
else die('Error - can not open file.');
 
die();

Open in new window

form.jpg
Avatar of kcalder
kcalder
Flag of United Kingdom of Great Britain and Northern Ireland image

ASKER

Oops, forgot to post the code for the form on the calling page....it'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">
					<?php 
					do { ?>
							<tr>
								<?php
								// 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; ?>" />
									<?php
									// 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 }
								else
								{ 
									// 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']; ?>
												</a>
										</td>
										<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 } ?>
							</tr>
					<?php } while ($row_rstDocList = mysql_fetch_assoc($rstDocList)); ?>
							<tr>
								
								<?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))" />
								</td>							
							</tr>
					<input type="hidden" name="MM_update" value="checkoutDoc" />
				</form>

Open in new window

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.


ASKER CERTIFIED SOLUTION
Avatar of Richard Quadling
Richard Quadling
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of kcalder

ASKER

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.