Multiple file upload using one form

Share on Google+0Share on Facebook0Tweet about this on TwitterEmail this to someone

I was intrigued by this topic in freestuff.gr (in Greek) to write this article.

The question is to develop a web page that will handle an events database. Although this is pretty simple by itself, issues arise when we want to attach a set of files to each event.

For simplicity’s sake, we will assume that an event has a unique id and a description field. Each attachment consists of an id as well, the event it belongs to, the original filename and the path to the locally stored copy. I want to make no assumptions about the data storage policy (RDBMS, filesystem, something else) so in our example we will emulate the DB using the $_SESSION variable:

session_start();
if(!isset($_SESSION['events']))
    $_SESSION['events']=array(
		65745=>array(
			'description'=>'An event',
			'files'=>array(
				4367=>array('filename'=>'image.png','path'=>uniqid()),
				),
			),
		266432=>array(
			'description'=>'Another event',
			'files'=>array(),
			),
		245964=>array(
			'description'=>'Yet another event',
			'files'=>array(
				6574=>array('filename'=>'document.doc','path'=>uniqid()),
				6346=>array('filename'=>'diagram.png','path'=>uniqid()),
				),
			),
		98656=>array(
			'description'=>'An event too',
			'files'=>array(
				5423456=>array('filename'=>'photo001.png','path'=>uniqid()),
				67547=>array('filename'=>'photo002.png','path'=>uniqid()),
				28454=>array('filename'=>'photo003.png','path'=>uniqid()),
				94376=>array('filename'=>'photo004.png','path'=>uniqid()),
				45634=>array('filename'=>'photo005.png','path'=>uniqid()),
				3246=>array('filename'=>'photo006.png','path'=>uniqid()),
				),
			),
		);

function get_events_list()
	{
	$r=array();
	foreach($_SESSION['events'] as $id=>$event)
		$r[]=array(
			'id'=>$id,
			'description'=>$event['description'],
			'filecount'=>count($event['files']),
			);
	return $r;
	}
	
function get_event($eventid)
	{
	if(isset($_SESSION['events'][$eventid]))
		return array(
			'id'=>$eventid,
			'description'=>$_SESSION['events'][$eventid]['description'],
			'files'=>$_SESSION['events'][$eventid]['files'],
			);
	else
		return array(
			'id'=>null,
			'description'=>'',
			'files'=>array(),
			);
	}
	
function update_event($event)
	{
	$_SESSION['events'][$event['id']]['description']=$event['description'];
	}
	
function insert_event($event)
	{
	$id=rand();
	$_SESSION['events'][$id]=array(
		'description'=>$event['description'],
		'files'=>array(),
		);
	return $id;
	}
	
function delete_event($eventid)
	{
	if(isset($_SESSION['events'][$eventid]))
		unset($_SESSION['events'][$eventid]);
	}
	
function insert_event_file($event,$filename,$path)
	{
	if(isset($_SESSION['events'][$event]))
		$_SESSION['events'][$event]['files'][rand()]=array(
			'filename'=>$filename,
			'path'=>$path,
			);
	}
    
function delete_event_file($eventid,$fileid)
	{
	if(isset($_SESSION['events'][$eventid]['files'][$fileid]))	
		unset($_SESSION['events'][$eventid]['files'][$fileid]);
	}
	

The above functions should be changed to implement the chosen storage policy.

If we want to delete an event, we will declare it in the query string, by giving delete={id}. Likewise, if we want to populate our form with a record for update, we will have to give event={id}. The inserts and the updates will be done using post. Folowing is the code that handles all these:

if(isset($_GET['delete'])) delete_event($_GET['delete']);
    
if(isset($_POST['id'])) // a form was posted
	{
	if($_POST['id'])
		{
		$id=$_POST['id'];
		update_event($_POST);
		if(isset($_POST['delfile']))
			foreach($_POST['delfile'] as $file)
				delete_event_file($id,$file);
		}
	else
		$id=insert_event($_POST);
	// insert uploaded files
	if(isset($_FILES['newfile']))
		for($i=0;$i<count($_FILES['newfile']['name']);$i++)
			if($_FILES['newfile']['name'][$i])
				insert_event_file(
					$id,
					$_FILES['newfile']['name'][$i],
					$_FILES['newfile']['tmp_name'][$i]
					);
	}

$cur_event=get_event(isset($_GET['event'])?$_GET['event']:null);

The first line’s operation is clear enough: If there’s a $_GET[‘delete’], the corresponding event should be deleted. Moreover, the last line loads the $cur_event variable with the event we specified we wanted to update. This, in turn, is used later to populate the form.

What’s interesting here is the $_POST fields. Obviously, if there is a $_POST[‘id’] then an update is implied, otherwise we must insert a new record. But the most important part is the way we handle the attachments’ inserts and deletes. There are two different fields, which by the way are arrays: $_POST[‘delfile’] contains the ids of the files to be deleted while $_FILES[‘newfile’] is an array of the newly uploaded files.

Now, let’s take a look at the html part. The page will be divided in two parts: a table listing the events and a form. If there’s a $_GET[‘event’], the form is filled with the specified record, otherwise it will accept a new one:

<!DOCTYPE html>
<html>
    <head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<title>Multifile Upload Form Demo</title>
		<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
		<script src="multifile.js"></script>
	</head>
	<body>
		<div id='dataset'>
			<h2>Events</h2>
			<table>
				<tr><th>id</th><th>Description</th><th>Files</th><th>Actions</th></tr>
				<?php foreach(get_events_list() as $row): ?>
					<tr>
						<td class='id'><?php echo $row['id']; ?></td>
						<td class='description'>
							<a href="?event=<?php echo $row['id']; ?>"><?php echo $row['description']; ?></a>
						</td>
						<td class='filecount'><?php echo $row['filecount']; ?></td>
						<td class='delete'>
							<a href="?delete=<?php echo $row['id']; ?>">Delete</a>
						</td>
					</tr>
				<?php endforeach; ?>
			</table>
		</div>
		<div id='eventform'>
			<h2>
				<?php if($cur_event['id']): ?>
					Edit Event <a href='?'>(Add new instead)</a>
				<? else: ?>
					New Event
				<?php endif; ?>
			</h2>
			<form method='post' enctype="multipart/form-data">
				<input type='hidden' name='id' value="<?php echo $cur_event['id']; ?>" />
				<label>
					Description:
					<input type='text' name='description' value="<?php echo $cur_event['description']?>" />
				</label>
				<fieldset>
					<legend>Files:</legend>
					<div>
						<table id='filelines'>
							<tr>
								<th>id</th>
								<th>Filename</th>
								<th>Path</th>
								<th>Actions</th>
							</tr>
							<?php foreach($cur_event['files'] as $id=>$file): ?>
								<tr>
									<td><?php echo $id; ?></td>
									<td><?php echo $file['filename']; ?></td>
									<td><?php echo $file['path']; ?></td>
									<td><button type='button' onclick="delFile(this,<?php echo $id; ?>);">-</button></td>
								</tr>
							<?php endforeach; ?>
						</table>
					</div>
					<button type='button' onclick='addFile();'>+</button>
				</fieldset>
				<input type='submit' value='Submit' />
			</form>
		</div>
	</body>
</html>

The only missing part is the implementation of two javascript functions: delFile() and addFile(). The former will delete the corresponding row from the attachments’ table and add a new delfile[] field as a hidden input, with the id of the file to be deleted. The latter will add a new row, complete with a newfile[] field to accomodate the new attachment. See the relevant code below:

function addFile()
    {
	file=$('<label>File:<input type="file" name="newfile[]" /></label>');
	delaction=$('<button type="button">-</button>');
	delaction.click(function(){$(this).closest('tr').remove();});
	$('#filelines').append(
		$('<tr>')
			.append($('<td>'))
			.append($('<td>').append(file))
			.append($('<td>'))
			.append($('<td>').append(delaction))
		);
	}
	
function delFile(el,id)
	{
	$(el)
		.closest('form')
		.append("<input type='hidden' name='delfile[]' value='"+id+"' />");
	$(el).closest('tr').remove();
	}

That’s about it. Comments are always welcome.

Happy Holiday!

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
Share on Google+0Share on Facebook0Tweet about this on TwitterEmail this to someone

Submit comment

Allowed HTML tags: <a href="http://google.com">google</a> <strong>bold</strong> <em>emphasized</em> <code>code</code> <blockquote>
quote
</blockquote>