Flow.js - Multiple simultaneous, stable and resumable uploads via the HTML5 File API in One Click Upload 7.2
It's a JavaScript library providing multiple simultaneous, stable and resumable uploads via the HTML5 File API.
The library is designed to introduce fault-tolerance into the upload of large files through HTTP. This is done by splitting each files into small chunks; whenever the upload of a chunk fails, uploading is retried until the procedure completes. This allows uploads to automatically resume uploading after a network connection is lost either locally or to the server. Additionally, it allows for users to pause and resume uploads without loosing state.
Flow.js relies on the HTML5 File API and the ability to chunks files into smaller pieces. Currently, this means that support is limited to Firefox 4+ and Chrome 11+.
Demo
Your browser, unfortunately, is not supported by Flow.js. The library requires support for the HTML5 File API along with file slicing.
File
flowjs/samples/Node.js/public/index.htmlView source
<!DOCTYPE html> <html> <head> <title>Flow.js - Multiple simultaneous, stable and resumable uploads via the HTML5 File API</title> <meta charset="utf-8" /> <link rel="stylesheet" type="text/css" href="style.css" /> </head> <body> <div id="frame"> <h1>Flow.js</h1> <p>It's a JavaScript library providing multiple simultaneous, stable and resumable uploads via the HTML5 File API.</p> <p>The library is designed to introduce fault-tolerance into the upload of large files through HTTP. This is done by splitting each files into small chunks; whenever the upload of a chunk fails, uploading is retried until the procedure completes. This allows uploads to automatically resume uploading after a network connection is lost either locally or to the server. Additionally, it allows for users to pause and resume uploads without loosing state.</p> <p>Flow.js relies on the HTML5 File API and the ability to chunks files into smaller pieces. Currently, this means that support is limited to Firefox 4+ and Chrome 11+.</p> <hr/> <h3>Demo</h3> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script src="flow.js"></script> <div class="flow-error"> Your browser, unfortunately, is not supported by Flow.js. The library requires support for <a href="http://www.w3.org/TR/FileAPI/">the HTML5 File API</a> along with <a href="http://www.w3.org/TR/FileAPI/#normalization-of-params">file slicing</a>. </div> <div class="flow-drop" ondragenter="jQuery(this).addClass('flow-dragover');" ondragend="jQuery(this).removeClass('flow-dragover');" ondrop="jQuery(this).removeClass('flow-dragover');"> Drop files here to upload or <a class="flow-browse-folder"><u>select folder</u></a> or <a class="flow-browse"><u>select from your computer</u></a> or <a class="flow-browse-image"><u>select images</u></a> </div> <div class="flow-progress"> <table> <tr> <td width="100%"><div class="progress-container"><div class="progress-bar"></div></div></td> <td class="progress-text" nowrap="nowrap"></td> <td class="progress-pause" nowrap="nowrap"> <a href="#" onclick="r.upload(); return(false);" class="progress-resume-link"><img src="resume.png" title="Resume upload" /></a> <a href="#" onclick="r.pause(); return(false);" class="progress-pause-link"><img src="pause.png" title="Pause upload" /></a> <a href="#" onclick="r.cancel(); return(false);" class="progress-cancel-link"><img src="cancel.png" title="Cancel upload" /></a> </td> </tr> </table> </div> <ul class="flow-list"></ul> <script> (function () { var r = new Flow({ target: '/upload', chunkSize: 1024*1024, testChunks: false }); // Flow.js isn't supported, fall back on a different method if (!r.support) { $('.flow-error').show(); return ; } // Show a place for dropping/selecting files $('.flow-drop').show(); r.assignDrop($('.flow-drop')[0]); r.assignBrowse($('.flow-browse')[0]); r.assignBrowse($('.flow-browse-folder')[0], true); r.assignBrowse($('.flow-browse-image')[0], false, false, {accept: 'image/*'}); // Handle file add event r.on('fileAdded', function(file){ // Show progress bar $('.flow-progress, .flow-list').show(); // Add the file to the list $('.flow-list').append( '<li class="flow-file flow-file-'+file.uniqueIdentifier+'">' + 'Uploading <span class="flow-file-name"></span> ' + '<span class="flow-file-size"></span> ' + '<span class="flow-file-progress"></span> ' + '<a href="" class="flow-file-download" target="_blank">' + 'Download' + '</a> ' + '<span class="flow-file-pause">' + ' <img src="pause.png" title="Pause upload" />' + '</span>' + '<span class="flow-file-resume">' + ' <img src="resume.png" title="Resume upload" />' + '</span>' + '<span class="flow-file-cancel">' + ' <img src="cancel.png" title="Cancel upload" />' + '</span>' ); var $self = $('.flow-file-'+file.uniqueIdentifier); $self.find('.flow-file-name').text(file.name); $self.find('.flow-file-size').text(readablizeBytes(file.size)); $self.find('.flow-file-download').attr('href', '/download/' + file.uniqueIdentifier).hide(); $self.find('.flow-file-pause').on('click', function () { file.pause(); $self.find('.flow-file-pause').hide(); $self.find('.flow-file-resume').show(); }); $self.find('.flow-file-resume').on('click', function () { file.resume(); $self.find('.flow-file-pause').show(); $self.find('.flow-file-resume').hide(); }); $self.find('.flow-file-cancel').on('click', function () { file.cancel(); $self.remove(); }); }); r.on('filesSubmitted', function(file) { r.upload(); }); r.on('complete', function(){ // Hide pause/resume when the upload has completed $('.flow-progress .progress-resume-link, .flow-progress .progress-pause-link').hide(); }); r.on('fileSuccess', function(file,message){ var $self = $('.flow-file-'+file.uniqueIdentifier); // Reflect that the file upload has completed $self.find('.flow-file-progress').text('(completed)'); $self.find('.flow-file-pause, .flow-file-resume').remove(); $self.find('.flow-file-download').attr('href', '/download/' + file.uniqueIdentifier).show(); }); r.on('fileError', function(file, message){ // Reflect that the file upload has resulted in error $('.flow-file-'+file.uniqueIdentifier+' .flow-file-progress').html('(file could not be uploaded: '+message+')'); }); r.on('fileProgress', function(file){ // Handle progress for both the file and the overall upload $('.flow-file-'+file.uniqueIdentifier+' .flow-file-progress') .html(Math.floor(file.progress()*100) + '% ' + readablizeBytes(file.averageSpeed) + '/s ' + secondsToStr(file.timeRemaining()) + ' remaining') ; $('.progress-bar').css({width:Math.floor(r.progress()*100) + '%'}); }); r.on('uploadStart', function(){ // Show pause, hide resume $('.flow-progress .progress-resume-link').hide(); $('.flow-progress .progress-pause-link').show(); }); r.on('catchAll', function() { console.log.apply(console, arguments); }); window.r = { pause: function () { r.pause(); // Show resume, hide pause $('.flow-file-resume').show(); $('.flow-file-pause').hide(); $('.flow-progress .progress-resume-link').show(); $('.flow-progress .progress-pause-link').hide(); }, cancel: function() { r.cancel(); $('.flow-file').remove(); }, upload: function() { $('.flow-file-pause').show(); $('.flow-file-resume').hide(); r.resume(); }, flow: r }; })(); function readablizeBytes(bytes) { var s = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB']; var e = Math.floor(Math.log(bytes) / Math.log(1024)); return (bytes / Math.pow(1024, e)).toFixed(2) + " " + s[e]; } function secondsToStr (temp) { function numberEnding (number) { return (number > 1) ? 's' : ''; } var years = Math.floor(temp / 31536000); if (years) { return years + ' year' + numberEnding(years); } var days = Math.floor((temp %= 31536000) / 86400); if (days) { return days + ' day' + numberEnding(days); } var hours = Math.floor((temp %= 86400) / 3600); if (hours) { return hours + ' hour' + numberEnding(hours); } var minutes = Math.floor((temp %= 3600) / 60); if (minutes) { return minutes + ' minute' + numberEnding(minutes); } var seconds = temp % 60; return seconds + ' second' + numberEnding(seconds); } </script> </div> </body> </html>