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.
This commit is contained in:
parent
aa70cbbf21
commit
c940d8d7ca
@ -8,10 +8,21 @@ require_once('include/photos.php');
|
|||||||
|
|
||||||
class Wall_attach extends \Zotlabs\Web\Controller {
|
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() {
|
function post() {
|
||||||
|
|
||||||
$using_api = false;
|
$using_api = false;
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
if($_REQUEST['api_source'] && array_key_exists('media',$_FILES)) {
|
if($_REQUEST['api_source'] && array_key_exists('media',$_FILES)) {
|
||||||
$using_api = true;
|
$using_api = true;
|
||||||
}
|
}
|
||||||
@ -29,6 +40,43 @@ class Wall_attach extends \Zotlabs\Web\Controller {
|
|||||||
if(! $channel)
|
if(! $channel)
|
||||||
killme();
|
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();
|
$observer = \App::get_observer();
|
||||||
|
|
||||||
|
|
||||||
@ -52,9 +100,13 @@ class Wall_attach extends \Zotlabs\Web\Controller {
|
|||||||
if($using_api)
|
if($using_api)
|
||||||
return $s;
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -100,77 +100,44 @@ function enableOnUser(){
|
|||||||
initEditor();
|
initEditor();
|
||||||
}
|
}
|
||||||
</script>
|
</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>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
/* enable tinymce on focus and click */
|
/* enable tinymce on focus and click */
|
||||||
$("#profile-jot-text").focus(enableOnUser);
|
$("#profile-jot-text").focus(enableOnUser);
|
||||||
$("#profile-jot-text").click(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
|
// call initialization file
|
||||||
if (window.File && window.FileList && window.FileReader) {
|
if (window.File && window.FileList && window.FileReader) {
|
||||||
DragDropUploadInit();
|
DragDropUploadInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function deleteCheckedItems() {
|
function deleteCheckedItems() {
|
||||||
@ -291,15 +258,17 @@ function enableOnUser(){
|
|||||||
|
|
||||||
function linkdrop(event) {
|
function linkdrop(event) {
|
||||||
var reply = event.dataTransfer.getData("text/uri-list");
|
var reply = event.dataTransfer.getData("text/uri-list");
|
||||||
event.preventDefault();
|
if(reply) {
|
||||||
var editwin = '#' + event.target.id;
|
event.preventDefault();
|
||||||
var commentwin = false;
|
var editwin = '#' + event.target.id;
|
||||||
if(editwin) {
|
var commentwin = false;
|
||||||
commentwin = ((editwin.indexOf('comment') >= 0) ? true : false);
|
if(editwin) {
|
||||||
if(commentwin) {
|
commentwin = ((editwin.indexOf('comment') >= 0) ? true : false);
|
||||||
var commentid = editwin.substring(editwin.lastIndexOf('-') + 1);
|
if(commentwin) {
|
||||||
commentOpen(document.getElementById(event.target.id),commentid);
|
var commentid = editwin.substring(editwin.lastIndexOf('-') + 1);
|
||||||
|
commentOpen(document.getElementById(event.target.id),commentid);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,8 +322,6 @@ function enableOnUser(){
|
|||||||
commentBusy = true;
|
commentBusy = true;
|
||||||
$('body').css('cursor', 'wait');
|
$('body').css('cursor', 'wait');
|
||||||
$.get('{{$baseurl}}/filer/' + id + '?term=' + reply, NavUpdate);
|
$.get('{{$baseurl}}/filer/' + id + '?term=' + reply, NavUpdate);
|
||||||
// if(timer) clearTimeout(timer);
|
|
||||||
// timer = setTimeout(NavUpdate,3000);
|
|
||||||
liking = 1;
|
liking = 1;
|
||||||
$('#item-filer-dialog').modal('hide');
|
$('#item-filer-dialog').modal('hide');
|
||||||
}
|
}
|
||||||
@ -499,32 +466,20 @@ function enableOnUser(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// initialize
|
// initialize drag-drop
|
||||||
function DragDropUploadInit() {
|
function DragDropUploadInit() {
|
||||||
|
|
||||||
var filedrag = $("#profile-jot-text");
|
var filedrag = $("#profile-jot-text");
|
||||||
|
|
||||||
// is XHR2 available?
|
// file drop
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
if (xhr.upload) {
|
|
||||||
|
|
||||||
// file drop
|
|
||||||
filedrag.on("dragover", DragDropUploadFileHover);
|
filedrag.on("dragover", DragDropUploadFileHover);
|
||||||
filedrag.on("dragleave", DragDropUploadFileHover);
|
filedrag.on("dragleave", DragDropUploadFileHover);
|
||||||
filedrag.on("drop", DragDropUploadFileSelectHandler);
|
filedrag.on("drop", DragDropUploadFileSelectHandler);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
window.filesToUpload = 0;
|
|
||||||
window.fileUploadsCompleted = 0;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// file drag hover
|
// file drag hover
|
||||||
function DragDropUploadFileHover(e) {
|
function DragDropUploadFileHover(e) {
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
e.target.className = (e.type == "dragover" ? "hover" : "");
|
e.target.className = (e.type == "dragover" ? "hover" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,49 +488,12 @@ function enableOnUser(){
|
|||||||
|
|
||||||
// cancel event and hover styling
|
// cancel event and hover styling
|
||||||
DragDropUploadFileHover(e);
|
DragDropUploadFileHover(e);
|
||||||
if (!editor) $("#profile-jot-text").val("");
|
// open editor if it isn't yet initialised
|
||||||
|
if (!editor) {
|
||||||
|
initEditor();
|
||||||
|
}
|
||||||
|
linkdrop(e);
|
||||||
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
var data = new FormData();
|
|
||||||
data.append('userfile', file);
|
|
||||||
xhr.send(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -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}}'>
|
<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}}
|
{{$mimeselect}}
|
||||||
{{$layoutselect}}
|
{{$layoutselect}}
|
||||||
@ -39,7 +40,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div id="jot-text-wrap">
|
<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>
|
</div>
|
||||||
{{if $attachment}}
|
{{if $attachment}}
|
||||||
<div id="jot-attachment-wrap">
|
<div id="jot-attachment-wrap">
|
||||||
|
Reference in New Issue
Block a user