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 “Queued Photo Uploader”

Showcase with converted FancyUpload.

The Action Happens Here

File Upload

This form is just an example fallback for the unobtrusive behaviour of FancyUpload. If this part is not changed, something must be wrong with your code.

    Don’t think that the uploader can handle only images! But since this showcase is called “Queued Photo Uploader”, the selectable file-types are limited to images. Check the showcase JavaScript shown under this paragraph for more options.

    What happens?

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

    1. Select one or more images.
    2. The images are uploaded one by one, overall progress and file progress is updated.
    3. The server-side PHP script logs the upload (see .log).
    4. A JSON response is returned, containing some information about the images like mime-type, width and height.
    5. This information is added to the completed file element. We would link the uploaded image, but we don’t save any files in this showcase.
    Do Not:
    Please Do:

    JavaScript & MooTools

    /**
     * FancyUpload Showcase
     *
     * @license		MIT License
     * @author		Harald Kirschner <mail [at] digitarald [dot] de>
     * @copyright	Authors
     */
     
    window.addEvent('domready', function() { // wait for the content
     
    	// our uploader instance 
     
    	var up = new FancyUpload2($('demo-status'), $('demo-list'), { // options object
    		// we console.log infos, remove that in production!!
    		verbose: true,
     
    		// url is read from the form, so you just have to change one place
    		url: $('form-demo').action,
     
    		// path to the SWF file
    		path: '../../source/Swiff.Uploader.swf',
     
    		// remove that line to select all files, or edit it, add more items
    		typeFilter: {
    			'Images (*.jpg, *.jpeg, *.gif, *.png)': '*.jpg; *.jpeg; *.gif; *.png'
    		},
     
    		// this is our browse button, *target* is overlayed with the Flash movie
    		target: 'demo-browse',
     
    		// graceful degradation, onLoad is only called if all went well with Flash
    		onLoad: function() {
    			$('demo-status').removeClass('hide'); // we show the actual UI
    			$('demo-fallback').destroy(); // ... and hide the plain form
     
    			// We relay the interactions with the overlayed flash to the link
    			this.target.addEvents({
    				click: function() {
    					return false;
    				},
    				mouseenter: function() {
    					this.addClass('hover');
    				},
    				mouseleave: function() {
    					this.removeClass('hover');
    					this.blur();
    				},
    				mousedown: function() {
    					this.focus();
    				}
    			});
     
    			// Interactions for the 2 other buttons
     
    			$('demo-clear').addEvent('click', function() {
    				up.remove(); // remove all files
    				return false;
    			});
     
    			$('demo-upload').addEvent('click', function() {
    				up.start(); // start upload
    				return false;
    			});
    		},
     
    		// Edit the following lines, it is your custom event handling
     
    		/**
    		 * Is called when files were not added, "files" is an array of invalid File classes.
    		 * 
    		 * This example creates a list of error elements directly in the file list, which
    		 * hide on click.
    		 */ 
    		onSelectFail: function(files) {
    			files.each(function(file) {
    				new Element('li', {
    					'class': 'validation-error',
    					html: file.validationErrorMessage || file.validationError,
    					title: MooTools.lang.get('FancyUpload', 'removeTitle'),
    					events: {
    						click: function() {
    							this.destroy();
    						}
    					}
    				}).inject(this.list, 'top');
    			}, this);
    		},
     
    		/**
    		 * This one was directly in FancyUpload2 before, the event makes it
    		 * easier for you, to add your own response handling (you probably want
    		 * to send something else than JSON or different items).
    		 */
    		onFileSuccess: function(file, response) {
    			var json = new Hash(JSON.decode(response, true) || {});
     
    			if (json.get('status') == '1') {
    				file.element.addClass('file-success');
    				file.info.set('html', '<strong>Image was uploaded:</strong> ' + json.get('width') + ' x ' + json.get('height') + 'px, <em>' + json.get('mime') + '</em>)');
    			} else {
    				file.element.addClass('file-failed');
    				file.info.set('html', '<strong>An error occured:</strong> ' + (json.get('error') ? (json.get('error') + ' #' + json.get('code')) : response));
    			}
    		},
     
    		/**
    		 * onFail is called when the Flash movie got bashed by some browser plugin
    		 * like Adblock or Flashblock.
    		 */
    		onFail: function(error) {
    			switch (error) {
    				case 'hidden': // works after enabling the movie and clicking refresh
    					alert('To enable the embedded uploader, unblock it in your browser and refresh (see Adblock).');
    					break;
    				case 'blocked': // This no *full* fail, it works after the user clicks the button
    					alert('To enable the embedded uploader, enable the blocked Flash movie (see Flashblock).');
    					break;
    				case 'empty': // Oh oh, wrong path
    					alert('A required file was not found, please be patient and we fix this.');
    					break;
    				case 'flash': // no flash 9+ :(
    					alert('To enable the embedded uploader, install the latest Adobe Flash plugin.')
    			}
    		}
     
    	});
     
    });

    XHTML Markup

    <form action="../script.php" method="post" enctype="multipart/form-data" id="form-demo">
     
    	<fieldset id="demo-fallback">
    		<legend>File Upload</legend>
    		<p>
    			This form is just an example fallback for the unobtrusive behaviour of FancyUpload.
    			If this part is not changed, something must be wrong with your code.
    		</p>
    		<label for="demo-photoupload">
    			Upload a Photo:
    			<input type="file" name="Filedata" />
    		</label>
    	</fieldset>
     
    	<div id="demo-status" class="hide">
    		<p>
    			<a href="#" id="demo-browse">Browse Files</a> |
    			<a href="#" id="demo-clear">Clear List</a> |
    			<a href="#" id="demo-upload">Start Upload</a>
    		</p>
    		<div>
    			<strong class="overall-title"></strong><br />
    			<img src="../../assets/progress-bar/bar.gif" class="progress overall-progress" />
    		</div>
    		<div>
    			<strong class="current-title"></strong><br />
    			<img src="../../assets/progress-bar/bar.gif" class="progress current-progress" />
    		</div>
    		<div class="current-text"></div>
    	</div>
     
    	<ul id="demo-list"></ul>
     
    </form>

    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

    /**
     * FancyUpload Showcase
     *
     * @license		MIT License
     * @author		Harald Kirschner <mail [at] digitarald [dot] de>
     * @copyright	Authors
     */
     
    /* CSS vs. Adblock tabs */
    .swiff-uploader-box a {
    	display: none !important;
    }
     
    /* .hover simulates the flash interactions */
    a:hover, a.hover {
    	color: red;
    }
     
    #demo-status {
    	padding: 10px 15px;
    	width: 420px;
    	border: 1px solid #eee;
    }
     
    #demo-status .progress {
    	background: url(../../assets/progress-bar/progress.gif) no-repeat;
    	background-position: +50% 0;
    	margin-right: 0.5em;
    	vertical-align: middle;
    }
     
    #demo-status .progress-text {
    	font-size: 0.9em;
    	font-weight: bold;
    }
     
    #demo-list {
    	list-style: none;
    	width: 450px;
    	margin: 0;
    }
     
    #demo-list li.validation-error {
    	padding-left: 44px;
    	display: block;
    	clear: left;
    	line-height: 40px;
    	color: #8a1f11;
    	cursor: pointer;
    	border-bottom: 1px solid #fbc2c4;
    	background: #fbe3e4 url(assets/failed.png) no-repeat 4px 4px;
    }
     
    #demo-list li.file {
    	border-bottom: 1px solid #eee;
    	background: url(assets/file.png) no-repeat 4px 4px;
    	overflow: auto;
    }
    #demo-list li.file.file-uploading {
    	background-image: url(assets/uploading.png);
    	background-color: #D9DDE9;
    }
    #demo-list li.file.file-success {
    	background-image: url(assets/success.png);
    }
    #demo-list li.file.file-failed {
    	background-image: url(assets/failed.png);
    }
     
    #demo-list li.file .file-name {
    	font-size: 1.2em;
    	margin-left: 44px;
    	display: block;
    	clear: left;
    	line-height: 40px;
    	height: 40px;
    	font-weight: bold;
    }
    #demo-list li.file .file-size {
    	font-size: 0.9em;
    	line-height: 18px;
    	float: right;
    	margin-top: 2px;
    	margin-right: 6px;
    }
    #demo-list li.file .file-info {
    	display: block;
    	margin-left: 44px;
    	font-size: 0.9em;
    	line-height: 20px;
    	clear
    }
    #demo-list li.file .file-remove {
    	clear: right;
    	float: right;
    	line-height: 18px;
    	margin-right: 6px;
    }

    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 (2121 Posts: )

    discussion by DISQUS 1231 Comments

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

    Sort:
    Comments 1 – 20 of 1231:
    • reply
      Avatar
      Piyush
      said 2 years ago (1 Point)
      This is good. Just an idea can we use Ajax to open multiple http connections and upload the files faster and simultaneously. Please post your comments on this Pros/ Cons.
    • reply
      Avatar
      Marek
      replied 1 year ago (1 Point)
      I have a problem with the uploader on Mac (both Firefox and Safari). On my site after I try to uplaod it the over progress goes to 100% straight away and each file has an error in status line: ioError #2038 and when I tested the one on this site I got another error: loading stops on 98% and the status says: httpStatus 301. ANY IDEAS?? :-)
    • reply
      Avatar
      digitarald Site Owner
      said 2 years ago (1 Point)
      Use the option queued for that and all uploads start at one time. But uploading queued is better for the server health and faster because the connection is not shared. Usually you also resize images or do other resource-eating calculations. Also the client-server connections are limited, to 3-4 connections. Of course simultaneous uploading looks cool, but confusing for the normal user.
    • reply
      Avatar
      Jay
      said 2 years ago (1 Point)
      Harald, I'm having an issue with using callbacks, namely the OnAllComplete callback. I've tried setting passing a "callBacks" object with the onAllComplete callback defined as a function, but that causes something to break (without an error), as the uploader never actually initializes. I also tried passing an onAllComplete to the constructor like you're using onLoad, but that isn't recognized (and nothing within it is used). Any brief examples for using the other callbacks?
    • reply
      Avatar
      digitarald Site Owner
      replied 2 years ago (1 Point)
      Can you paste some more code here, so I can make sure that I understand what you are describing and what code u are using.
    • reply
      Avatar
      said 2 years ago (1 Point)
      How I can send POST data with the fileupload?
      I user mod_rewrite for my website, so I can't use GET...

      I can't find a documentation...

      Bist du eigentlich ein Deutscher? Im alten Code waren deutsche Kommentare xD
    • reply
      Avatar
      digitarald Site Owner
      replied 2 years ago (1 Point)
      Use options: data (defined in Swiff.Uploader) or return {data: ...} from onBeforeOpen to set POST data for a single file. I'll add documentation for that later when the API is stable. The .de in the domain is not just camouflage ;)
    • reply
      Avatar
      Bodom78 5 Points
      said 2 years ago (1 Point)
      Have not tried it out personally but playing with the demo it worked great for me in FF 2.0.0.14 and IE6/7.

      This is a really nice integration of JavaScript and Flash, great work.
    • reply
      Avatar
      jyuza 1 Point
      said 2 years ago (1 Point)
      First of all, I would like to express my gratitude to all of those who helped each other on the previous version of FancyUpload. Thanks to you guys, specially Digitarald for his tremendous programming skills and for all the time he spends, helping people, like me, with no real knowledge in Javascript.

      I was using the previous version of FancyUpload and I am trying to use the brand new one. Right now I got it work, but I had to copy the Mootools.js in the example to make it work because the Mootools version don't work. That was the only problem I had (maybe it will help, who knows).

      And here comes the question. Like someone who posted in the comments, I am trying to post Data along with the Files I upload. My problem is that I have a <select> items, and I would like to send Image with the Id I would select in the list of items. How can I proceed ? Thank you very much my friends, J.</select>
    • reply
      Avatar
      jojo
      said 2 years ago (1 Point)
      hello,

      Who can explain me how to send POST data while uploading ? Thanks !!!!
    • reply
      Avatar
      jojo
      said 2 years ago (1 Point)
      I see your message "Use options: data (defined in Swiff.Uploader) or return {data: …} from onBeforeOpen to set POST data" but i do not understund what i have to return...

      thanks
    • reply
      Avatar
      Jay
      said 2 years ago (1 Point)
      Harald, here's the pastebin:

      http://paste.mootools.net/fa929569

      (small note, I clicked the reply link under your post, but nothing seemed to happen. Hopefully this shows as a reply to your post, but no guarantees I guess :) )
    • reply
      Avatar
      digitarald Site Owner
      replied 2 years ago (1 Point)
      It did not, and I just "repaired" the reply button. Maybe the disqus people add some admin interface to re-sort the messages, so I can clean up the comments-reply relations :)

      At this time you can't give additional callbacks. To override the behaviour, extend the FancyUpload2 class (lets see how markdown works in the comments :D

      var MyPhotoUpload = new Class({

      Extends: FancyUpload2,

      onAllComplete: function(current) {
      this.parent(current);
      // No alert, its callback from flash, don't make crazy things here because Flash waits for the return
      console.log('Done!');
      }
      });

      btw.: Indent your code with 4 spaces or 1 tab for code/pre. This is a test for disqus, if it plays nice with the spaces.
    • reply
      Avatar
      lookatthosemoose 1 Point
      said 2 years ago (0 Point)
      For those attempting to send additional form data, see this line in Swiff.Uploader.js:

      if ($type(options.data) == 'element') options.data = $(options.data).toQueryString();

      So, that means that if you have a form w/ id 'upload_ticket' you can do this to gain access to $_POST array in your upload script.

      var swiffy = new FancyUpload2($('demo-status'), $('demo-list'), {
      'url': $('form-demo').action,
      'data': $('upload_ticket'), //will convert the form to query string.
      'fieldName': 'upload',
      'path': 'swiff/Swiff.Uploader.swf',
      'onLoad': function() {
      $('demo-status').removeClass('hide');
      $('demo-fallback').destroy();
      }
      });

      Took me a lil while to figure out, so I thought it might help everyone else.

      Harald -- Rock on. version 2 kicks arse.
    • reply
      Avatar
      lookatthosemoose 1 Point
      said 2 years ago (2 Points)
      sorry for the comment spam, here's the code for initializing FancyUpload2 w/ a form as additional data:

      FancyUpload2($(‘demo-status’), $(‘demo-list’), {
      ‘url’: $(‘form-demo’).action,
      ‘data’: $(‘upload_ticket’), //will convert the form to query string.
      ‘fieldName’: ‘upload’,
      ‘path’: ‘swiff/Swiff.Uploader.swf’,
      ‘onLoad’: function() {
      $(‘demo-status’).removeClass(‘hide’); $(‘demo-fallback’).destroy();
      }
      });
    • reply
      Avatar
      digitarald Site Owner
      replied 2 years ago (1 Point)
      I can clean that up ... [**markdown-documentation**](http://daringfireball.net/projects/markdown/syntax). (btw, disqus allows editing your own comments and you are registered)
    • reply
      Avatar
      said 2 years ago (1 Point)
      how can i check if there are upload errors.. no logs written and no file was uploaded but i set the right permission to the folder..
    • reply
      Avatar
      digitarald Site Owner
      replied 2 years ago (1 Point)
      No log and no file, sounds like an invalid path. Check your server log for the request, tell me what FancyUpload shows as report after the upload and eventually [paste](http://paste.mootools.net) the js-code u use.
    • reply
      Avatar
      aqlx86 1 Point
      replied 2 years ago (1 Point)
      got it working already.. i have a typo error on my scripts. thnx
    • reply
      Avatar
      jyuza 1 Point
      said 2 years ago (1 Point)
      Good morning everyone.

      Again, thanks for your advices I was able to make the data sending work. Now I have another problem related to this data I post to my upload.php.
      Actually, I have to select items in a select to get an id and then send the data to the proper item. Problem is the form seems to keep the first value I got from the select, no matter what I select afterwhile.

      Is there a way to 'unload' the class ? Or a way to select different data to send ?

      Thank yo so much my friends

      J.
    Comments 1 – 20 of 1231:

    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