Add support for new file upload mechanics

This commit is contained in:
Dane Everitt 2016-10-13 21:03:49 -04:00
parent f65e41a1af
commit e5ffb15020
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
4 changed files with 97 additions and 92 deletions

View file

@ -3,7 +3,7 @@ This file is a running track of new features and fixes to each version of the pa
This project follows [Semantic Versioning](http://semver.org) guidelines.
## v0.5.0-pre.2 (Bodacious Boreopterus)
## v0.5.0-pre.3 (Bodacious Boreopterus)
### Added
* Return node configuration from remote API by using `/api/nodes/{id}/config` endpoint. Only accepts SSL connections.
@ -13,6 +13,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
### Changed
* Creating a user, server, or node now returns `HTTP/1.1 200` and a JSON element with the user/server/node's ID.
* Environment setting script is much more user friendly and does not require an excessive amount of clicking and typing.
* File upload method switched from BinaryJS to Socket.io implementation to fix bugs as well as be a little speedier and allow upload throttling.
## v0.5.0-pre.2 (Bodacious Boreopterus)

File diff suppressed because one or more lines are too long

15
public/js/vendor/upload/client.min.js vendored Normal file
View file

@ -0,0 +1,15 @@
/* Socket IO File Upload Client-Side Library
* Copyright (C) 2015 Shane Carr and others
* Released under the X11 License
* For more information, visit: https://github.com/vote539/socketio-file-upload
*/
(function(g,d,f){"function"===typeof define&&define.amd?define(d,f):"object"===typeof module&&module.exports?module.exports=f():g[d]=f()})(this,"SocketIOFileUpload",function(){return function(g){var d=this;if(!window.File||!window.FileReader)throw Error("Socket.IO File Upload: Browser Not Supported");var f={},q=[],u=[],k={};d.fileInputElementId="siofu_input";d.resetFileInputs=!0;d.useText=!1;d.serializedOctets=!1;d.useBuffer=!0;d.chunkSize=102400;d.chunkDelay=0;var r=function(a,b){var c=document.createEvent("Event");
c.initEvent(a,!1,!1);for(var y in b)b.hasOwnProperty(y)&&(c[y]=b[y]);return d.dispatchEvent(c)},p=[],e=function(a,b,c,d){a.addEventListener(b,c,d);p.push(arguments)},t=function(a,b,c,d){a.removeEventListener&&a.removeEventListener(b,c,d)},z=function(){for(var a=p.length-1;0<=a;a--)t.apply(this,p[a]);p=[]},A=function(a){if(null!==d.maxFileSize&&a.size>d.maxFileSize)r("error",{file:a,message:"Attempt by client to upload file exceeding the maximum file size",code:1});else if(r("start",{file:a})){var b=
new FileReader,c=q.length,f=d.useText,v=0,k;b._realReader&&(b=b._realReader);q.push(a);var p={id:c},w=d.chunkSize;if(w>=a.size||0>=w)w=a.size;var x=function(){if(!p.abort){var c=a.slice(v,Math.min(v+w,a.size));f?b.readAsText(c):b.readAsArrayBuffer(c)}},l=function(e){if(!p.abort){var l=Math.min(v+w,a.size);a:{var q=v;e=e.target.result;var u=!1;if(!f)try{var m=new Uint8Array(e);if(d.serializedOctets)e=m;else if(d.useBuffer)e=m.buffer;else{var u=!0,n,t=m.buffer.byteLength,h="";for(n=0;n<t;n+=3)h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[m[n]>>
2],h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(m[n]&3)<<4|m[n+1]>>4],h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(m[n+1]&15)<<2|m[n+2]>>6],h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[m[n+2]&63];2===t%3?h=h.substring(0,h.length-1)+"=":1===t%3&&(h=h.substring(0,h.length-2)+"==");e=h}}catch(z){g.emit("siofu_done",{id:c,interrupt:!0});break a}g.emit("siofu_progress",{id:c,size:a.size,start:q,end:l,content:e,base64:u})}r("progress",
{file:a,bytesLoaded:l,name:k});v+=w;v<a.size?setTimeout(x,d.chunkDelay):(g.emit("siofu_done",{id:c}),r("load",{file:a,reader:b,name:k}))}};e(b,"load",l);e(b,"error",function(){g.emit("siofu_done",{id:c,interrupt:!0});t(b,"load",l)});e(b,"abort",function(){g.emit("siofu_done",{id:c,interrupt:!0});t(b,"load",l)});g.emit("siofu_start",{name:a.name,mtime:a.lastModifiedDate,meta:a.meta,size:a.size,encoding:f?"text":"octet",id:c});u.push(function(a){k=a;x()});return p}},x=function(a){if(0!==a.length){for(var b=
0;b<a.length;b++)a[b].meta||(a[b].meta={});if(r("choose",{files:a}))for(b=0;b<a.length;b++){var c=A(a[b]);k[c.id]=c}}},l=function(a){var b=a.target.files||a.dataTransfer.files;a.preventDefault();x(b);if(d.resetFileInputs){try{a.target.value=""}catch(e){}if(a.target.value){var b=document.createElement("form"),c=a.target.parentNode,f=a.target.nextSibling;b.appendChild(a.target);b.reset();c.insertBefore(a.target,f)}}};this.submitFiles=function(a){a&&x(a)};this.listenOnSubmit=function(a,b){b.files&&e(a,
"click",function(){x(b.files)},!1)};this.listenOnArraySubmit=function(a,b){for(var c in b)this.listenOnSubmit(a,b[c])};this.listenOnInput=function(a){a.files&&e(a,"change",l,!1)};this.listenOnDrop=function(a){e(a,"dragover",function(a){a.preventDefault()},!1);e(a,"drop",l)};this.prompt=function(){var a;a=document.getElementById(d.fileInputElementId);a||(a=document.createElement("input"),a.setAttribute("type","file"),a.setAttribute("id",d.fileInputElementId),a.style.display="none",document.body.appendChild(a));
e(a,"change",l,!1);var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,0,0,!1,!1,!1,!1,0,null);a.dispatchEvent(b)};this.destroy=function(){z();var a=document.getElementById(d.fileInputElementId);a&&a.parentNode.removeChild(a);for(var b in k)k.hasOwnProperty(b)&&(k[b].abort=!0);k=u=q=f=null};this.addEventListener=function(a,b){f[a]||(f[a]=[]);f[a].push(b)};this.removeEventListener=function(a,b){if(!f[a])return!1;for(var c=0;c<f[a].length;c++)if(f[a][c]===b)return f[a].splice(c,
1),!0;return!1};this.dispatchEvent=function(a){var b=f[a.type];if(!b)return!0;for(var c=!0,d=0;d<b.length;d++)!1===b[d](a)&&(c=!1);return c};e(g,"siofu_ready",function(a){u[a.id](a.name)});e(g,"siofu_complete",function(a){r("complete",{file:q[a.id],detail:a.detail,success:a.success})});e(g,"siofu_error",function(a){r("error",{file:q[a.id],message:a.message,code:0});k[a.id].abort=!0})}});

View file

@ -25,7 +25,7 @@
@section('scripts')
@parent
{!! Theme::js('js/binaryjs.js') !!}
{!! Theme::js('js/vendor/upload/client.min.js') !!}
{!! Theme::js('js/vendor/lodash/lodash.js') !!}
@endsection
@ -102,11 +102,11 @@
</div>
</div>
<div class="alert alert-warning">Edit the path location above <strong>before you upload files</strong>. They will automatically be placed in the directory you specify above. You can change this each time you upload a new file without having to press anything else.</div>
<div class="alert alert-warning">Edit the path location above <strong>before you upload files</strong>. They will automatically be placed in the directory you specify above. You can change this each time you upload a new file without having to press anything else. <em>The directory must exist before performing an upload.</em></div>
<div class="alert alert-danger" id="upload_error" style="display: none;"></div>
<input type="file" id="fileinput" name="fileUpload[]" multiple="" style="display:none;"/>
<div id="uploader_box" class="well well-sm" style="cursor:pointer;">
<center><h2 style="margin-bottom: 25px;">Connecting...</h2></center>
<center><h2 style="margin-bottom: 25px;">Drag and Drop File Here</h2></center>
</div>
<span id="file_progress"></span>
</div>
@ -124,99 +124,90 @@ $(window).load(function () {
var newFileContents;
@can('upload-files', $server)
var client = new BinaryClient('{{ $node->scheme === 'https' ? 'wss' : 'ws' }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/upload/', {
chunkSize: 40960
var notifyUploadSocketError = false;
var uploadSocket = io('{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/upload/{{ $server->uuid }}', {
'query': 'token={{ $server->daemonSecret }}'
});
// Wait for connection to BinaryJS server
client.on('open', function() {
var box = $('#uploader_box');
box.on('dragenter', doNothing);
box.on('dragover', doNothing);
box.html('<center><h2 style="margin-bottom:25px;">Drag or Click to Upload</h2></center>');
box.on('click', function () {
$('#fileinput').click();
socket.io.on('connect_error', function (err) {
siofu.destroy();
$('#applyUpdate').removeClass('fa-circle-o-notch fa-spinner fa-spin').addClass('fa-question-circle').css({ color: '#FF9900' });
if(typeof notifyUploadSocketError !== 'object') {
notifyUploadSocketError = $.notify({
message: 'There was an error connecting to the Upload Socket for this server.'
}, {
type: 'danger',
delay: 0
});
box.on('drop', function (e, files) {
if (typeof files !== 'undefined') {
e.originalEvent = {
dataTransfer: {
files: files.currentTarget.files
}
};
});
uploadSocket.on('error', err => {
siofu.destroy();
console.error(err);
});
uploadSocket.on('connect', function () {
if (notifyUploadSocketError !== false) {
notifyUploadSocketError.close();
notifyUploadSocketError = false;
}
});
// e.preventDefault();
$.each(e.originalEvent.dataTransfer.files, function(index, value) {
socket.on('error', function (err) {
console.error('There was an error while attemping to connect to the websocket: ' + err + '\n\nPlease try loading this page again.');
});
var file = e.originalEvent.dataTransfer.files[index];
var identifier = Math.random().toString(36).slice(2);
$('#file_progress').append('<div class="well well-sm" id="file-upload-' + identifier +'"> \
var siofu = new SocketIOFileUpload(uploadSocket);
siofu.chunkDelay = 50;
document.getElementById("uploader_box").addEventListener("click", siofu.prompt, false);
siofu.listenOnDrop(document.getElementById("uploader_box"));
siofu.addEventListener('start', function (event) {
event.file.meta.path = $("#u_file_name").val();
event.file.meta.identifier = Math.random().toString(36).slice(2);
$('#file_progress').append('<div class="well well-sm" id="file-upload-' + event.file.meta.identifier +'"> \
<div class="row"> \
<div class="col-md-12"> \
<h6>Uploading ' + file.name + '</h6> \
<span class="prog-bar-text-' + identifier +'" style="font-size: 10px;position: absolute;margin: 3px 0 0 15px;">Waiting...</span> \
<h6>Uploading ' + event.file.name + '</h6> \
<span class="prog-bar-text-' + event.file.meta.identifier +'" style="font-size: 10px;position: absolute;margin: 3px 0 0 15px;">Waiting...</span> \
<div class="progress progress-striped active"> \
<div class="progress-bar progress-bar-info prog-bar-' + identifier +'" style="width: 0%"></div> \
<div class="progress-bar progress-bar-info prog-bar-' + event.file.meta.identifier +'" style="width: 0%"></div> \
</div> \
</div> \
</div> \
</div>');
// Add to list of uploaded files
var stream = client.send(file, {
token: "{{ $server->daemonSecret }}",
server: "{{ $server->uuid }}",
path: $("#u_file_name").val(),
name: file.name,
size: file.size
});
var tx = 0;
stream.on('data', function(data) {
if(data.error) {
$("#upload_error").html(data.error).show();
$("#file-upload-" + identifier).hide();
siofu.addEventListener('progress', function(event) {
console.log(event.file);
var percent = event.bytesLoaded / event.file.size * 100;
if (percent >= 100) {
$('.prog-bar-text-' + event.file.meta.identifier).text('Upload Complete');
$('.prog-bar-' + event.file.meta.identifier).css('width', '100%').removeClass('progress-bar-info').addClass('progress-bar-success').parent().removeClass('active progress-striped');
$('.prog-bar-text-' + event.file.meta.identifier).parents().eq(2).delay(5000).slideUp();
} else {
tx += data.rx;
if(tx >= 0.999) {
$('.prog-bar-text-' + identifier).text('Upload Complete');
$('.prog-bar-' + identifier).css('width', '100%').parent().removeClass('active').removeClass('progress-striped');
} else {
$('.prog-bar-text-' + identifier).text(Math.round(tx * 100) + '%');
$('.prog-bar-' + identifier).css('width', tx * 100 + '%');
}
$('.prog-bar-text-' + event.file.meta.identifier).text(Math.round(percent) + '%');
$('.prog-bar-' + event.file.meta.identifier).css('width', percent + '%');
}
});
stream.on('close', function(data) {
$("#upload_error").html("The BinaryJS data stream was closed by the server. Please refresh the page and try again.").show();
$("#file-upload-" + identifier).hide();
});
stream.on('error', function(data) {
console.error("An error was encountered with the BinaryJS upload stream.");
});
});
});
});
// listen for a file being chosen
$('#fileinput').change(function (event) {
$('#uploader_box').trigger('drop', [event, event.currentTarget]);
$('#fileinput').val('');
});
// Deal with DOM quirks
function doNothing (e){
e.preventDefault();
e.stopPropagation();
// Do something when a file is uploaded:
siofu.addEventListener('complete', function(event){
if (!event.success) {
$("#upload_error").html('An error was encountered while attempting to upload this file. Does the target directory exist?').show();
$("#file-upload-" + event.file.meta.identifier).hide();
}
});
siofu.addEventListener('error', function(event){
$("#upload_error").html('An error was encountered while attempting to upload this file. Does the target directory exist?').show();
$("#file-upload-" + event.file.meta.identifier).hide();
});
@endcan
const Editor = ace.edit('fileContents');