diff options
author | zotlabs <mike@macgirvin.com> | 2017-07-13 17:04:58 -0700 |
---|---|---|
committer | zotlabs <mike@macgirvin.com> | 2017-07-13 17:04:58 -0700 |
commit | c940d8d7ca25691519d400ad19aa22b5611fc2e7 (patch) | |
tree | b33805ab6561bc312845be52c9eb0a141e60912a | |
parent | aa70cbbf21950e37ab9b5198de76ded0f1c8351d (diff) | |
download | volse-hubzilla-c940d8d7ca25691519d400ad19aa22b5611fc2e7.tar.gz volse-hubzilla-c940d8d7ca25691519d400ad19aa22b5611fc2e7.tar.bz2 volse-hubzilla-c940d8d7ca25691519d400ad19aa22b5611fc2e7.zip |
implement chunked uploads on the wall; making it work painlessly on /cloud was attempted but will not be implemented today. That presents some interesting dragons to slay.
-rw-r--r-- | Zotlabs/Module/Wall_attach.php | 64 | ||||
-rw-r--r-- | include/attach.php | 101 | ||||
-rwxr-xr-x | view/tpl/jot-header.tpl | 172 | ||||
-rwxr-xr-x | view/tpl/jot.tpl | 3 |
4 files changed, 206 insertions, 134 deletions
diff --git a/Zotlabs/Module/Wall_attach.php b/Zotlabs/Module/Wall_attach.php index c6fe7518e..5b3768da9 100644 --- a/Zotlabs/Module/Wall_attach.php +++ b/Zotlabs/Module/Wall_attach.php @@ -8,10 +8,21 @@ require_once('include/photos.php'); class Wall_attach extends \Zotlabs\Web\Controller { + function init() { + logger('request_method: ' . $_SERVER['REQUEST_METHOD'],LOGGER_DATA,LOG_INFO); + logger('wall_attach: ' . print_r($_REQUEST,true),LOGGER_DEBUG,LOG_INFO); + logger('wall_attach files: ' . print_r($_FILES,true),LOGGER_DEBUG,LOG_INFO); + // for testing without actually storing anything + // http_status_exit(200,'OK'); + } + + function post() { $using_api = false; - + + $result = []; + if($_REQUEST['api_source'] && array_key_exists('media',$_FILES)) { $using_api = true; } @@ -28,7 +39,44 @@ class Wall_attach extends \Zotlabs\Web\Controller { if(! $channel) killme(); - + + $matches = []; + $partial = false; + + $x = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); + if($x) { + // logger('Content-Range: ' . print_r($matches,true)); + $partial = true; + } + + if($partial) { + $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]); + if($x['partial']) { + header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); + json_return_and_die($result); + } + else { + $_FILES['userfile'] = [ + 'name' => $x['name'], + 'type' => $x['type'], + 'tmp_name' => $x['tmp_name'], + 'error' => $x['error'], + 'size' => $x['size'] + ]; + } + } + else { + if(! array_key_exists('userfile',$_FILES)) { + $_FILES['userfile'] = [ + 'name' => $_FILES['files']['name'], + 'type' => $_FILES['files']['type'], + 'tmp_name' => $_FILES['files']['tmp_name'], + 'error' => $_FILES['files']['error'], + 'size' => $_FILES['files']['size'] + ]; + } + } + $observer = \App::get_observer(); @@ -51,10 +99,14 @@ class Wall_attach extends \Zotlabs\Web\Controller { if($using_api) return $s; - - echo $s; - killme(); - + + + if($partial) + header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); + $result['message'] = $s; + json_return_and_die($result); + } + } diff --git a/include/attach.php b/include/attach.php index ac50b05b1..3a0c8b7ba 100644 --- a/include/attach.php +++ b/include/attach.php @@ -2355,3 +2355,104 @@ function attach_upgrade() { } } + +function save_chunk($channel,$start,$end,$len) { + + $result = []; + + $tmp_path = $_FILES['files']['tmp_name']; + $new_base = 'store/[data]/' . $channel['channel_address'] . '/tmp'; + os_mkdir($new_base,STORAGE_DEFAULT_PERMISSIONS,true); + + $new_path = $new_base . '/' . $_FILES['files']['name']; + + if(! file_exists($new_path)) { + rename($tmp_path,$new_path); + } + else { + $istream = fopen($tmp_path,'rb'); + $ostream = fopen($new_path,'ab'); + if($istream && $ostream) { + pipe_streams($istream,$ostream); + fclose($istream); + fclose($ostream); + } + } + if(($len - 1) == $end) { + unlink($tmp_path); + $result['name'] = $_FILES['files']['tmp_name']; + $result['type'] = $_FILES['files']['type']; + $result['tmp_name'] = $new_path; + $result['error'] = 0; + $result['size'] = $len; + $result['complete'] = true; + return $result; + } + $result['partial'] = true; + $result['length'] = intval(filesize($new_path)); + return $result; +} + + +/* + * chunkloader + * Submit handler for chunked uploads + * + */ + +function chunkloader($channel,$arr) { + + logger('request: ' . print_r($arr,true), LOGGER_DEBUG); + logger('files: ' . print_r($_FILES,true), LOGGER_DEBUG); + + + $result = []; + + + $tmp_path = $_FILES['file']['tmp_name']; + $new_base = 'store/[data]/' . $channel['channel_address'] . '/tmp'; + os_mkdir($new_base,STORAGE_DEFAULT_PERMISSIONS,true); + + $new_path = $new_base . '/' . $arr['resumableFilename']; + + rename($tmp_path,$new_path . '.' . intval($arr['resumableChunkNumber'])); + + $missing_parts = false; + for($x = 1; $x <= intval($arr['resumableTotalChunks']); $x ++) { + if(! file_exists($new_path . '.' . $x)) { + $missing_parts = true; + break; + } + } + + if($missing_parts) { + $result['partial'] = true; + return $result; + } + + if(intval($arr['resumableTotalChunks']) === 1) { + rename($new_path . '.' . '1', $new_path); + } + else { + for($x = 1; $x <= intval($arr['resumableTotalChunks']); $x ++) { + $istream = fopen($new_path . '.' . $x,'rb'); + $ostream = fopen($new_path,'ab'); + if($istream && $ostream) { + pipe_streams($istream,$ostream); + fclose($istream); + fclose($ostream); + } + unlink($new_path . '.' . $x); + } + } + + $result['name'] = $arr['resumableFilename']; + $result['type'] = $arr['resumableType']; + $result['tmp_name'] = $new_path; + $result['error'] = 0; + $result['size'] = $arr['resumableTotalSize']; + $result['complete'] = true; + return $result; + +} + diff --git a/view/tpl/jot-header.tpl b/view/tpl/jot-header.tpl index 1cc5a898a..d8f296aa4 100755 --- a/view/tpl/jot-header.tpl +++ b/view/tpl/jot-header.tpl @@ -100,77 +100,44 @@ function enableOnUser(){ initEditor(); } </script> -<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script> + +<script src="library/blueimp_upload/js/vendor/jquery.ui.widget.js"></script> +<script src="library/blueimp_upload/js/jquery.iframe-transport.js"></script> +<script src="library/blueimp_upload/js/jquery.fileupload.js"></script> + <script> $(document).ready(function() { /* enable tinymce on focus and click */ $("#profile-jot-text").focus(enableOnUser); $("#profile-jot-text").click(enableOnUser); - var upload_title = $('#wall-image-upload').attr('title'); - var attach_title = $('#wall-file-upload').attr('title'); - try { - var uploader = new window.AjaxUpload('wall-image-upload', - { action: '{{$baseurl}}/wall_upload/{{$nickname}}', - name: 'userfile', - title: upload_title, - onSubmit: function(file,ext) { $('#profile-rotator').spin('tiny'); }, - onComplete: function(file,response) { - addeditortext(response); - $('#jot-media').val($('#jot-media').val() + response); - $('#profile-rotator').spin(false); - } - }); - } catch (e) { - } - try { - var uploader_sub = new window.AjaxUpload('wall-image-upload-sub', - { action: '{{$baseurl}}/wall_upload/{{$nickname}}', - name: 'userfile', - title: upload_title, - onSubmit: function(file,ext) { $('#profile-rotator').spin('tiny'); }, - onComplete: function(file,response) { - addeditortext(response); - $('#jot-media').val($('#jot-media').val() + response); - $('#profile-rotator').spin(false); - } - }); - } catch(e) { - } - try { - var file_uploader = new window.AjaxUpload('wall-file-upload', - { action: '{{$baseurl}}/wall_attach/{{$nickname}}', - name: 'userfile', - title: attach_title, - onSubmit: function(file,ext) { $('#profile-rotator').spin('tiny'); }, - onComplete: function(file,response) { - addeditortext(response); - $('#jot-media').val($('#jot-media').val() + response); - $('#profile-rotator').spin(false); - } - }); - } catch(e) { - } - try { - var file_uploader_sub = new window.AjaxUpload('wall-file-upload-sub', - { action: '{{$baseurl}}/wall_attach/{{$nickname}}', - name: 'userfile', - title: attach_title, - onSubmit: function(file,ext) { $('#profile-rotator').spin('tiny'); }, - onComplete: function(file,response) { - addeditortext(response); - $('#jot-media').val($('#jot-media').val() + response); - $('#profile-rotator').spin(false); - } - }); - } catch(e) { - } - - + + $('#invisible-wall-file-upload').fileupload({ + url: 'wall_attach/{{$nickname}}', + dataType: 'json', + dropZone: $('#profile-jot-text'), + maxChunkSize: 4 * 1024 * 1024, + add: function(e,data) { + $('#profile-rotator').spin('tiny'); + data.submit(); + }, + done: function(e,data) { + addeditortext(data.result.message); + $('#jot-media').val($('#jot-media').val() + data.result.message); + }, + stop: function(e,data) { + $('#profile-rotator').spin(false); + }, + }); + + $('#wall-file-upload').click(function(event) { event.preventDefault(); $('#invisible-wall-file-upload').trigger('click'); return false;}); + $('#wall-file-upload-sub').click(function(event) { event.preventDefault(); $('#invisible-wall-file-upload').trigger('click'); return false;}); + // call initialization file if (window.File && window.FileList && window.FileReader) { DragDropUploadInit(); } + }); function deleteCheckedItems() { @@ -291,15 +258,17 @@ function enableOnUser(){ function linkdrop(event) { var reply = event.dataTransfer.getData("text/uri-list"); - event.preventDefault(); - var editwin = '#' + event.target.id; - var commentwin = false; - if(editwin) { - commentwin = ((editwin.indexOf('comment') >= 0) ? true : false); - if(commentwin) { - var commentid = editwin.substring(editwin.lastIndexOf('-') + 1); - commentOpen(document.getElementById(event.target.id),commentid); + if(reply) { + event.preventDefault(); + var editwin = '#' + event.target.id; + var commentwin = false; + if(editwin) { + commentwin = ((editwin.indexOf('comment') >= 0) ? true : false); + if(commentwin) { + var commentid = editwin.substring(editwin.lastIndexOf('-') + 1); + commentOpen(document.getElementById(event.target.id),commentid); + } } } @@ -353,8 +322,6 @@ function enableOnUser(){ commentBusy = true; $('body').css('cursor', 'wait'); $.get('{{$baseurl}}/filer/' + id + '?term=' + reply, NavUpdate); -// if(timer) clearTimeout(timer); -// timer = setTimeout(NavUpdate,3000); liking = 1; $('#item-filer-dialog').modal('hide'); } @@ -499,32 +466,20 @@ function enableOnUser(){ }; // - // initialize + // initialize drag-drop function DragDropUploadInit() { var filedrag = $("#profile-jot-text"); - // is XHR2 available? - var xhr = new XMLHttpRequest(); - if (xhr.upload) { - - // file drop + // file drop filedrag.on("dragover", DragDropUploadFileHover); filedrag.on("dragleave", DragDropUploadFileHover); filedrag.on("drop", DragDropUploadFileSelectHandler); - } - - window.filesToUpload = 0; - window.fileUploadsCompleted = 0; - - } // file drag hover function DragDropUploadFileHover(e) { - e.stopPropagation(); - e.preventDefault(); e.target.className = (e.type == "dragover" ? "hover" : ""); } @@ -533,49 +488,12 @@ function enableOnUser(){ // cancel event and hover styling DragDropUploadFileHover(e); - if (!editor) $("#profile-jot-text").val(""); - - - // fetch FileList object - var files = e.target.files || e.originalEvent.dataTransfer.files; - // process all File objects - for (var i = 0, f; f = files[i]; i++) { - DragDropUploadFile(f, i); - } - - } - - // upload files - function DragDropUploadFile(file, idx) { - - window.filesToUpload = window.filesToUpload + 1; - - var xhr = new XMLHttpRequest(); - xhr.withCredentials = true; // Include the SESSION cookie info for authentication - (xhr.upload || xhr).addEventListener('progress', function (e) { - $('#profile-rotator').spin('tiny'); - }); - xhr.addEventListener('load', function (e) { - //console.log('xhr upload complete', e); - window.fileUploadsCompleted = window.fileUploadsCompleted + 1; - - initEditor(function() { - addeditortext(xhr.responseText); - }); - - $('#jot-media').val($('#jot-media').val() + xhr.responseText); - // When all the uploads have completed, refresh the page - if (window.filesToUpload > 0 && window.fileUploadsCompleted === window.filesToUpload) { - $('#profile-rotator').spin(false); - window.fileUploadsCompleted = window.filesToUpload = 0; - } - }); - // POST to the wall_upload endpoint - xhr.open('post', '{{$baseurl}}/wall_attach/{{$nickname}}', true); + // open editor if it isn't yet initialised + if (!editor) { + initEditor(); + } + linkdrop(e); - var data = new FormData(); - data.append('userfile', file); - xhr.send(data); } </script> diff --git a/view/tpl/jot.tpl b/view/tpl/jot.tpl index dbbb470c5..cf99ede42 100755 --- a/view/tpl/jot.tpl +++ b/view/tpl/jot.tpl @@ -1,3 +1,4 @@ +<input id="invisible-wall-file-upload" type="file" name="files" style="visibility:hidden;position:absolute;top:-50;left:-50;width:0;height:0;" multiple> <form id="profile-jot-form" action="{{$action}}" method="post" class="acl-form" data-form_id="profile-jot-form" data-allow_cid='{{$allow_cid}}' data-allow_gid='{{$allow_gid}}' data-deny_cid='{{$deny_cid}}' data-deny_gid='{{$deny_gid}}'> {{$mimeselect}} {{$layoutselect}} @@ -39,7 +40,7 @@ </div> {{/if}} <div id="jot-text-wrap"> - <textarea class="profile-jot-text" id="profile-jot-text" name="body" tabindex="2" placeholder="{{$share}}" ondragenter="linkdropper(event);" ondragover="linkdropper(event);" ondrop="linkdrop(event);" >{{$content}}</textarea> + <textarea class="profile-jot-text" id="profile-jot-text" name="body" tabindex="2" placeholder="{{$share}}" >{{$content}}</textarea> </div> {{if $attachment}} <div id="jot-attachment-wrap"> |