From c940d8d7ca25691519d400ad19aa22b5611fc2e7 Mon Sep 17 00:00:00 2001 From: zotlabs Date: Thu, 13 Jul 2017 17:04:58 -0700 Subject: 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. --- Zotlabs/Module/Wall_attach.php | 64 +++++++++++++-- include/attach.php | 101 ++++++++++++++++++++++++ view/tpl/jot-header.tpl | 172 +++++++++++------------------------------ 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(); } - + + + + + 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 @@ +
{{$mimeselect}} {{$layoutselect}} @@ -39,7 +40,7 @@ {{/if}}
- +
{{if $attachment}}
-- cgit v1.2.3