Other versions:

FancyUpload - Swiff meets Ajax (v3.0)

Project FancyUpload - Swiff meets Ajax Showcase Swiff meets Ajax for powerful and elegant uploads. FancyUpload is a file-input replacement which features an unobtrusive, multiple-file selection menu and queued upload with an animated progress bar. It is easy to setup, is server independent, completely styleable via CSS and XHTML and uses MooTools to work in all modern browsers.

Showcases:

Showcase “Single File Button”

A simple implementation of the Swiff.Uploader class

The Action Happens Here

What happens?

The PHP script does not save the files but logs every request. Check the Firebug console for event debugging logs.

  1. Select an image smaller than 2 Mb.
    • If the file is too big, the uploader gives you a warning.
    • After selecting a file, the browse button gets disabled.
  2. The image is uploaded, the button changes its text to the progress info.
  3. The server-side PHP script logs the upload (see .log).
    • This demo appends the browsers cookie data automatically (option appendCookieData), therefore the POST values contain the SID.
  4. A JSON response is returned, including a MD5 hash, generated from the file content.
  5. The hash is used to get a monsterid from gravatar, so every file has an unique gravatar (nothing actually useful, just to show off the server-side processing, since we don’t save the file)

Update messages are presented by Roar Notifications.

JavaScript & MooTools

/**
 * FancyUpload Showcase
 *
 * @license		MIT License
 * @author		Harald Kirschner <mail [at] digitarald [dot] de>
 * @copyright	Authors
 */
 
window.addEvent('domready', function() {
 
	// One Roar instance for our notofications, positioned in the top-right corner of our demo.
	var log = new Roar({
		container: $('demo'),
		position: 'topRight',
		duration: 5000
	});
 
	var link = $('select-0');
	var linkIdle = link.get('html');
 
	function linkUpdate() {
		if (!swf.uploading) return;
		var size = Swiff.Uploader.formatUnit(swf.size, 'b');
		link.set('html', '<span class="small">' + swf.percentLoaded + '% of ' + size + '</span>');
	}
 
	// Uploader instance
	var swf = new Swiff.Uploader({
		path: '../../source/Swiff.Uploader.swf',
		url: '../script.php',
		verbose: true,
		queued: false,
		multiple: false,
		target: link,
		instantStart: true,
		typeFilter: {
			'Images (*.jpg, *.jpeg, *.gif, *.png)': '*.jpg; *.jpeg; *.gif; *.png'
		},
		fileSizeMax: 2 * 1024 * 1024,
		onSelectSuccess: function(files) {
			if (Browser.Platform.linux) window.alert('Warning: Due to a misbehaviour of Adobe Flash Player on Linux,\nthe browser will probably freeze during the upload process.\nSince you are prepared now, the upload will start right away ...');
			log.alert('Starting Upload', 'Uploading <em>' + files[0].name + '</em> (' + Swiff.Uploader.formatUnit(files[0].size, 'b') + ')');
			this.setEnabled(false);
		},
		onSelectFail: function(files) {
			log.alert('<em>' + files[0].name + '</em> was not added!', 'Please select an image smaller than 2 Mb. (Error: #' + files[0].validationError + ')');
		},
		appendCookieData: true,
		onQueue: linkUpdate,
		onFileComplete: function(file) {
 
			// We *don't* save the uploaded images, we only take the md5 value and create a monsterid ;)
			if (file.response.error) {
				log.alert('Failed Upload', 'Uploading <em>' + this.fileList[0].name + '</em> failed, please try again. (Error: #' + this.fileList[0].response.code + ' ' + this.fileList[0].response.error + ')');
			} else {
				var md5 = JSON.decode(file.response.text, true).hash; // secure decode
 
				log.alert('Successful Upload', 'an MD5 hash was created from <em>' + this.fileList[0].name + '</em>: <code>' + md5 + '</code>.<br />gravatar.com generated a fancy and unique monsterid for it, since we did not save the image.');
 
				var img = $('demo-portrait');
				img.setStyle('background-image', img.getStyle('background-image').replace(/\w{32}/, md5));
				img.highlight();
			}
 
			file.remove();
			this.setEnabled(true);
		},
		onComplete: function() {
			link.set('html', linkIdle);
		}
	});
 
	// Button state
	link.addEvents({
		click: function() {
			return false;
		},
		mouseenter: function() {
			this.addClass('hover');
			swf.reposition();
		},
		mouseleave: function() {
			this.removeClass('hover');
			this.blur();
		},
		mousedown: function() {
			this.focus();
		}
	});
 
});
 

XHTML Markup

<div id="demo-portrait" style="background-image: url(http://www.gravatar.com/avatar/ad4c26a01dc657d9b4fc3dc9c8299927?s=128&amp;d=monsterid)">
	<a href="#" id="select-0" title="Please upload only images, maximal 2 Mb filesize!">Upload new Photo</a>
</div>

PHP Script

<?php
/**
 * Swiff.Uploader Example Backend
 *
 * This file represents a simple logging, validation and output.
 *  *
 * WARNING: If you really copy these lines in your backend without
 * any modification, there is something seriously wrong! Drop me a line
 * and I can give you a good rate for fancy and customised installation.
 *
 * No showcase represents 100% an actual real world file handling,
 * you need to move and process the file in your own code!
 * Just like you would do it with other uploaded files, nothing
 * special.
 *
 * @license		MIT License
 *
 * @author		Harald Kirschner <mail [at] digitarald [dot] de>
 * @copyright	Authors
 *
 */
 
 
/**
 * Only needed if you have a logged in user, see option appendCookieData,
 * which adds session id and other available cookies to the sent data.
 *
 * session_name('SID'); // whatever your session name is, adapt that!
 * session_start();
 */
 
// Request log
 
/**
 * You don't need to log, this is just for the showcase. Better remove
 * those lines for production since the log contains detailed file
 * information.
 */
 
$result = array();
 
$result['time'] = date('r');
$result['addr'] = substr_replace(gethostbyaddr($_SERVER['REMOTE_ADDR']), '******', 0, 6);
$result['agent'] = $_SERVER['HTTP_USER_AGENT'];
 
if (count($_GET)) {
	$result['get'] = $_GET;
}
if (count($_POST)) {
	$result['post'] = $_POST;
}
if (count($_FILES)) {
	$result['files'] = $_FILES;
}
 
// we kill an old file to keep the size small
if (file_exists('script.log') && filesize('script.log') > 102400) {
	unlink('script.log');
}
 
$log = @fopen('script.log', 'a');
if ($log) {
	fputs($log, print_r($result, true) . "\n---\n");
	fclose($log);
}
 
 
// Validation
 
$error = false;
 
if (!isset($_FILES['Filedata']) || !is_uploaded_file($_FILES['Filedata']['tmp_name'])) {
	$error = 'Invalid Upload';
}
 
/**
 * You would add more validation, checking image type or user rights.
 *
 
if (!$error && $_FILES['Filedata']['size'] > 2 * 1024 * 1024)
{
	$error = 'Please upload only files smaller than 2Mb!';
}
 
if (!$error && !($size = @getimagesize($_FILES['Filedata']['tmp_name']) ) )
{
	$error = 'Please upload only images, no other files are supported.';
}
 
if (!$error && !in_array($size[2], array(1, 2, 3, 7, 8) ) )
{
	$error = 'Please upload only images of type JPEG, GIF or PNG.';
}
 
if (!$error && ($size[0] < 25) || ($size[1] < 25))
{
	$error = 'Please upload an image bigger than 25px.';
}
*/
 
 
// Processing
 
/**
 * Its a demo, you would move or process the file like:
 *
 * move_uploaded_file($_FILES['Filedata']['tmp_name'], '../uploads/' . $_FILES['Filedata']['name']);
 * $return['src'] = '/uploads/' . $_FILES['Filedata']['name'];
 *
 * or
 *
 * $return['link'] = YourImageLibrary::createThumbnail($_FILES['Filedata']['tmp_name']);
 *
 */
 
if ($error) {
 
	$return = array(
		'status' => '0',
		'error' => $error
	);
 
} else {
 
	$return = array(
		'status' => '1',
		'name' => $_FILES['Filedata']['name']
	);
 
	// Our processing, we get a hash value from the file
	$return['hash'] = md5_file($_FILES['Filedata']['tmp_name']);
 
	// ... and if available, we get image data
	$info = @getimagesize($_FILES['Filedata']['tmp_name']);
 
	if ($info) {
		$return['width'] = $info[0];
		$return['height'] = $info[1];
		$return['mime'] = $info['mime'];
	}
 
}
 
 
// Output
 
/**
 * Again, a demo case. We can switch here, for different showcases
 * between different formats. You can also return plain data, like an URL
 * or whatever you want.
 *
 * The Content-type headers are uncommented, since Flash doesn't care for them
 * anyway. This way also the IFrame-based uploader sees the content.
 */
 
if (isset($_REQUEST['response']) && $_REQUEST['response'] == 'xml') {
	// header('Content-type: text/xml');
 
	// Really dirty, use DOM and CDATA section!
	echo '<response>';
	foreach ($return as $key => $value) {
		echo "<$key><![CDATA[$value]]></$key>";
	}
	echo '</response>';
} else {
	// header('Content-type: application/json');
 
	echo json_encode($return);
}
 
?>

CSS Stylesheet

/* Basic layout */
 
h4 {
	margin-top: 1.25em;
}
 
a {
	padding: 1px;
}
 
a:hover, a.hover {
	color: red;
}
 
/* demo elements */
 
#demo-portrait {
	float: left;
	position: relative;
	width: 130px;
	height: 153px;
	border: 1px solid #eee;
	background-position: 1px 1px;
	background-repeat: no-repeat;
}
 
#demo-portrait a {
	position: absolute;
	left: 1px;
	right: 1px;
	bottom: 1px;
	padding: 0;
	line-height: 22px;
	display: block;
	text-align: center;
}

This example and the accompanying sources/assets are © 2008-2009 by Harald Kirschner and available under The MIT License. For debugging and profiling the scripts and their markup download Firefox and use addons like Firebug and Web Developer Toolbar.

Share it: Stumble it!Digg This!del.icio.us (No Posts)

discussion by DISQUS No Comments

Please use the support forums for discussing the project, asking questions or posting bug-fixes!

Sort:
No Comments
  • First
  • ‹ Prev
  • Next ›
  • Last
No Comments
  • First
  • ‹ Prev
  • Next ›
  • Last

Post your comment

Please use the support forums for discussing the project, asking questions or posting bug-fixes!


Internet Consultant & Contractor

I'm available to combine forces with you and your team to find the most simple, elegant and convenient web solutions . I await your call.

If you just like my work and want to say Thank You, donate via PayPal or Amazon Wish List.

Developer Resources & Tools