FancyUpload - Swiff meets Ajax (v3.0)
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.
Showcase “Attach a File”
A reduced and snappier interface (preview).
The Action Happens Here
Experimental interface preview, not fully tested yet. Inspired by a real-world usage, used in a well-known web-application. Imagine it wrapped in a form to compose e-mails …
Enabled Validations
- Selectable file-size is limited to 2 MB
What will happen?
- Selecting multiple files
- Instant start
- All files start at once (no queued upload)
- Cancel and remove single file
- Validate size
JavaScript & MooTools
/** * FancyUpload Showcase * * @license MIT License * @author Harald Kirschner <mail [at] digitarald [dot] de> * @copyright Authors */ window.addEvent('domready', function() { /** * Uploader instance */ var up = new FancyUpload3.Attach('demo-list', '#demo-attach, #demo-attach-2', { path: '../../source/Swiff.Uploader.swf', url: '../script.php', fileSizeMax: 2 * 1024 * 1024, verbose: true, onSelectFail: function(files) { files.each(function(file) { new Element('li', { 'class': 'file-invalid', events: { click: function() { this.destroy(); } } }).adopt( new Element('span', {html: file.validationErrorMessage || file.validationError}) ).inject(this.list, 'bottom'); }, this); }, onFileSuccess: function(file) { new Element('input', {type: 'checkbox', 'checked': true}).inject(file.ui.element, 'top'); file.ui.element.highlight('#e6efc2'); }, onFileError: function(file) { file.ui.cancel.set('html', 'Retry').removeEvents().addEvent('click', function() { file.requeue(); return false; }); new Element('span', { html: file.errorMessage, 'class': 'file-error' }).inject(file.ui.cancel, 'after'); }, onFileRequeue: function(file) { file.ui.element.getElement('.file-error').destroy(); file.ui.cancel.set('html', 'Cancel').removeEvents().addEvent('click', function() { file.remove(); return false; }); this.start(); } }); });
XHTML Markup
<a href="#" id="demo-attach">Attach a file</a> <ul id="demo-list"></ul> <a href="#" id="demo-attach-2" style="display: none;">Attach another file</a>
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
a.hover { color: red; } #demo-list { padding: 0; list-style: none; margin: 0; } #demo-list .file-invalid { cursor: pointer; color: #514721; padding-left: 48px; line-height: 24px; background: url(assets/error.png) no-repeat 24px 5px; margin-bottom: 1px; } #demo-list .file-invalid span { background-color: #fff6bf; padding: 1px; } #demo-list .file { line-height: 2em; padding-left: 22px; background: url(assets/attach.png) no-repeat 1px 50%; } #demo-list .file span, #demo-list .file a { padding: 0 4px; } #demo-list .file .file-size { color: #666; } #demo-list .file .file-error { color: #8a1f11; } #demo-list .file .file-progress { width: 125px; height: 12px; vertical-align: middle; background-image: url(../../assets/progress-bar/progress.gif); }
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.
1231 Comments
Please use the support forums for discussing the project, asking questions or posting bug-fixes!