parent
e3b3bdb92f
commit
28fee3c731
@ -0,0 +1,76 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
/** |
||||||
|
* Input file with progress element. |
||||||
|
* |
||||||
|
* Class BigUpload |
||||||
|
*/ |
||||||
|
class BigUpload extends HTML_QuickForm_file |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @param string $elementName |
||||||
|
* @param string $elementLabel |
||||||
|
* @param array $attributes |
||||||
|
*/ |
||||||
|
public function __construct($elementName = null, $elementLabel = null, $attributes = null) |
||||||
|
{ |
||||||
|
parent::__construct($elementName, $elementLabel, $attributes); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function toHtml() |
||||||
|
{ |
||||||
|
$origin = $this->getAttribute('data-origin'); |
||||||
|
$id = $this->getAttribute('id'); |
||||||
|
$html = parent::toHtml(); |
||||||
|
$html .= '<div id="'.$id.'-bigUploadProgressBarContainer"> |
||||||
|
<div id="'.$id.'-bigUploadProgressBarFilled"></div> |
||||||
|
</div> |
||||||
|
<div id="'.$id.'-bigUploadTimeRemaining"></div> |
||||||
|
<div id="'.$id.'-bigUploadResponse"></div>'; |
||||||
|
$js = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'bigupload/js/bigUpload.js"></script>'; |
||||||
|
$js .= '<script> |
||||||
|
var bigUpload = new bigUpload(); |
||||||
|
var uploadForm, formId, submitButtonId; |
||||||
|
$(function() { |
||||||
|
uploadForm = $("#'.$id.'").closest("form"); |
||||||
|
formId = uploadForm.attr("id"); |
||||||
|
submitButtonId = uploadForm.find("[type=\'submit\']").attr("id"); |
||||||
|
$("#"+submitButtonId).click(function(e) { |
||||||
|
e.preventDefault(); |
||||||
|
setBigUploadSettings(); |
||||||
|
bigUpload.fire(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
function setBigUploadSettings() { |
||||||
|
//The id of the file input |
||||||
|
bigUpload.settings.inputField = "'.$id.'"; |
||||||
|
//The id of the form with the file upload. |
||||||
|
bigUpload.settings.formId = formId; |
||||||
|
//The id of the progress bar |
||||||
|
bigUpload.settings.progressBarField = "'.$id.'-bigUploadProgressBarFilled"; |
||||||
|
//The id of the time remaining field |
||||||
|
bigUpload.settings.timeRemainingField = "'.$id.'-bigUploadTimeRemaining"; |
||||||
|
//The id of the text response field |
||||||
|
bigUpload.settings.responseField = "'.$id.'-bigUploadResponse"; |
||||||
|
//The id of the submit button |
||||||
|
bigUpload.settings.submitButton = submitButtonId; |
||||||
|
//Color of the background of the progress bar |
||||||
|
bigUpload.settings.progressBarColor = "#5bb75b"; |
||||||
|
//Color of the background of the progress bar when an error is triggered |
||||||
|
bigUpload.settings.progressBarColorError = "#da4f49"; |
||||||
|
//Path to the php script for handling the uploads |
||||||
|
bigUpload.settings.scriptPath = "'.api_get_path(WEB_LIBRARY_JS_PATH).'bigupload/inc/bigUpload.php"; |
||||||
|
//Set the origin upload |
||||||
|
bigUpload.settings.origin = "'.$origin.'"; |
||||||
|
//The parameters from the upload form |
||||||
|
bigUpload.settings.formParams = uploadForm.serialize(); |
||||||
|
} |
||||||
|
</script>'; |
||||||
|
|
||||||
|
return $js.$html; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,53 @@ |
|||||||
|
------------------------------------------------------------------------- |
||||||
|
|
||||||
|
BigUpload |
||||||
|
|
||||||
|
version 1.2 |
||||||
|
Created by: Sean Thielen <sean@p27.us> |
||||||
|
[BigUpload: Uploading really big files in the browser](http://p27.us/2013/03/bigupload-uploading-really-big-files-in-the-browser/) |
||||||
|
|
||||||
|
------------------------------------------------------------------------- |
||||||
|
|
||||||
|
BigUpload is a tool for handling large file uploads (tested up to 2GB) through the browser. |
||||||
|
|
||||||
|
 |
||||||
|
|
||||||
|
------------------------------------------------------------------------- |
||||||
|
|
||||||
|
It uses the HTML5 FileReader library to split large files into manageable chunks, |
||||||
|
and then sends these chunks to the server one at a time using an XmlHttpRequest. |
||||||
|
|
||||||
|
The php script then pieces these chunks together into one large file. |
||||||
|
|
||||||
|
Because the chunks are all the same size, it is easy to calculate an accurate progress bar |
||||||
|
and a fairly accurate time remaining variable. |
||||||
|
|
||||||
|
This tool is capable of handling file uploads of up to 2GB in size, without the need to tweak |
||||||
|
the max_upload and timeout variables on your httpd. |
||||||
|
|
||||||
|
This tool only works on Chrome and Firefox, but falls back to a normal file upload form on other browsers. |
||||||
|
|
||||||
|
If you want to deploy this as-is, the variables you need to worry about are in the top of |
||||||
|
* js/bigUpload.js |
||||||
|
* inc/bigUpload.php |
||||||
|
|
||||||
|
And you need to be sure to make /BigUpload/files and /BigUpload/files/tmp writeable |
||||||
|
|
||||||
|
|
||||||
|
Please feel free to contribute and use this in your projects! |
||||||
|
|
||||||
|
------------------------------------------------------------------------- |
||||||
|
|
||||||
|
v 1.2 |
||||||
|
* Cleaned up the code quite a lot |
||||||
|
* Added pause/resume functionality |
||||||
|
* Added fallback for unsupported browsers |
||||||
|
|
||||||
|
v 1.0.1 |
||||||
|
* Added time remaining calculator |
||||||
|
* Response from php script is now a json object, allowing for error processing |
||||||
|
* Minor script changes and bugfixes |
||||||
|
* Better comments |
||||||
|
|
||||||
|
v 1.0.0 |
||||||
|
* Initial version |
||||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 37 KiB |
@ -0,0 +1,78 @@ |
|||||||
|
body { |
||||||
|
margin: 0 auto; |
||||||
|
background-color:#ffffff; |
||||||
|
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; |
||||||
|
color: #000000; |
||||||
|
font-size: 12px; |
||||||
|
} |
||||||
|
h1 { |
||||||
|
font-size:32px; |
||||||
|
margin-top:0; |
||||||
|
} |
||||||
|
.bigUpload .bigUploadContainer { |
||||||
|
max-width: 350px; |
||||||
|
padding: 20px 30px 30px; |
||||||
|
margin: 0 auto 20px; |
||||||
|
margin-top:75px; |
||||||
|
background-color: #ffffff; |
||||||
|
border: 1px solid #e5e5e5; |
||||||
|
-webkit-border-radius: 5px; |
||||||
|
-moz-border-radius: 5px; |
||||||
|
border-radius: 5px; |
||||||
|
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05); |
||||||
|
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05); |
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,.05); |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
.bigUpload #bigUploadFile { |
||||||
|
border: 1px solid #e5e5e5; |
||||||
|
-webkit-border-radius: 5px; |
||||||
|
-moz-border-radius: 5px; |
||||||
|
border-radius: 5px; |
||||||
|
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05); |
||||||
|
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05); |
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,.05); |
||||||
|
padding: 10px; |
||||||
|
} |
||||||
|
.bigUpload .bigUploadButton { |
||||||
|
background-color: rgb(0, 109, 204); |
||||||
|
border: 1px solid rgba(0, 0, 0, 0.09); |
||||||
|
-webkit-border-radius: 6px; |
||||||
|
-moz-border-radius: 6px; |
||||||
|
border-radius:6px; |
||||||
|
padding-bottom: 11px; |
||||||
|
padding-left: 19px; |
||||||
|
padding-right: 19px; |
||||||
|
padding-top: 11px; |
||||||
|
font-size: 18px; |
||||||
|
color:#ffffff; |
||||||
|
cursor: pointer; |
||||||
|
margin-top:15px; |
||||||
|
font-size: 18px; |
||||||
|
} |
||||||
|
.bigUpload .bigUploadAbort { |
||||||
|
background-color:rgb(218, 79, 73); |
||||||
|
} |
||||||
|
|
||||||
|
.bigUpload #bigUploadProgressBarContainer { |
||||||
|
width:94%; |
||||||
|
height:19px; |
||||||
|
margin-left: 3%; |
||||||
|
margin-top:1%; |
||||||
|
border: 1px solid #e5e5e5; |
||||||
|
-webkit-border-radius: 6px; |
||||||
|
-moz-border-radius: 6px; |
||||||
|
border-radius: 6px; |
||||||
|
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05); |
||||||
|
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05); |
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,.05); |
||||||
|
} |
||||||
|
|
||||||
|
.bigUpload #bigUploadProgressBarFilled { |
||||||
|
border-radius: 5px; |
||||||
|
margin: 1px; |
||||||
|
height:17px; |
||||||
|
width:0; |
||||||
|
background-color: rgb(91, 183, 91); |
||||||
|
font-size: 14px; |
||||||
|
} |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
|
||||||
|
.bigUploadContainer{ |
||||||
|
width : 450px; |
||||||
|
border : solid 1px gray; |
||||||
|
border-radius: 14px; |
||||||
|
padding : 20px; |
||||||
|
} |
||||||
|
|
||||||
|
.bigUploadContainer input[type="file"] { |
||||||
|
appearance: auto; |
||||||
|
user-select: none; |
||||||
|
white-space: pre; |
||||||
|
align-items: flex-start; |
||||||
|
text-align: center; |
||||||
|
cursor: default; |
||||||
|
padding: 1px 6px; |
||||||
|
margin : 8px; |
||||||
|
padding : 8px; |
||||||
|
padding-left : 2px; |
||||||
|
margin-left : 2px; |
||||||
|
} |
||||||
|
|
||||||
|
.bigUploadButton{ |
||||||
|
margin-left : 16px; |
||||||
|
} |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
<!doctype html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<link rel="stylesheet" href="style.css"> |
||||||
|
<script src="js/bigUpload.js"></script> |
||||||
|
<script> |
||||||
|
bigUpload = new bigUpload(); |
||||||
|
function upload() { |
||||||
|
bigUpload.fire(); |
||||||
|
} |
||||||
|
function abort() { |
||||||
|
bigUpload.abortFileUpload(); |
||||||
|
} |
||||||
|
</script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div class="bigUpload"> |
||||||
|
<div class="bigUploadContainer"> |
||||||
|
<h1>BigUpload</h1> |
||||||
|
<form action="inc/bigUpload.php?action=post-unsupported" method="post" enctype="multipart/form-data" id="bigUploadForm"> |
||||||
|
<input type="file" id="bigUploadFile" name="bigUploadFile" /> |
||||||
|
<input type="button" class="bigUploadButton" value="Start Upload" id="bigUploadSubmit" onclick="upload()" /> |
||||||
|
<input type="button" class="bigUploadButton bigUploadAbort" value="Cancel" onclick="abort()" /> |
||||||
|
</form> |
||||||
|
<div id="bigUploadProgressBarContainer"> |
||||||
|
<div id="bigUploadProgressBarFilled"> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div id="bigUploadTimeRemaining"></div> |
||||||
|
<div id="bigUploadResponse"></div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</body> |
||||||
|
</html> |
||||||
@ -0,0 +1,302 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
require_once '../../../../global.inc.php'; |
||||||
|
require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php'; |
||||||
|
|
||||||
|
class BigUpload |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Temporary directory for uploading files |
||||||
|
*/ |
||||||
|
const TEMP_DIRECTORY = '/tmp/'; |
||||||
|
|
||||||
|
/** |
||||||
|
* Directory files will be moved to after the upload is completed |
||||||
|
*/ |
||||||
|
const MAIN_DIRECTORY = '../files/'; |
||||||
|
|
||||||
|
/** |
||||||
|
* Max allowed filesize. This is for unsupported browsers and |
||||||
|
* as an additional security check in case someone bypasses the js filesize check. |
||||||
|
* |
||||||
|
* This must match the value specified in main.js |
||||||
|
*/ |
||||||
|
const MAX_SIZE = 2147483648; |
||||||
|
|
||||||
|
/** |
||||||
|
* Temporary directory |
||||||
|
* @var string |
||||||
|
*/ |
||||||
|
private $tempDirectory; |
||||||
|
|
||||||
|
/** |
||||||
|
* Directory for completed uploads |
||||||
|
* @var string |
||||||
|
*/ |
||||||
|
private $mainDirectory; |
||||||
|
|
||||||
|
/** |
||||||
|
* Name of the temporary file. Used as a reference to make sure chunks get written to the right file. |
||||||
|
* @var string |
||||||
|
*/ |
||||||
|
private $tempName; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor function, sets the temporary directory and main directory |
||||||
|
*/ |
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$tempDirectory = api_get_path(SYS_ARCHIVE_PATH); |
||||||
|
$this->setTempDirectory($tempDirectory); |
||||||
|
$this->setMainDirectory(self::MAIN_DIRECTORY); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a random file name for the file to use as it's being uploaded |
||||||
|
* @param string $value Temporary filename |
||||||
|
*/ |
||||||
|
public function setTempName($value = null) |
||||||
|
{ |
||||||
|
if($value) { |
||||||
|
$this->tempName = $value; |
||||||
|
} |
||||||
|
else { |
||||||
|
$this->tempName = mt_rand() . '.tmp'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the name of the temporary file |
||||||
|
* @return string Temporary filename |
||||||
|
*/ |
||||||
|
public function getTempName() |
||||||
|
{ |
||||||
|
return $this->tempName; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the name of the temporary directory |
||||||
|
* @param string $value Temporary directory |
||||||
|
*/ |
||||||
|
public function setTempDirectory($value) |
||||||
|
{ |
||||||
|
$this->tempDirectory = $value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the name of the temporary directory |
||||||
|
* @return string Temporary directory |
||||||
|
*/ |
||||||
|
public function getTempDirectory() |
||||||
|
{ |
||||||
|
return $this->tempDirectory; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the name of the main directory |
||||||
|
* @param string $value Main directory |
||||||
|
*/ |
||||||
|
public function setMainDirectory($value) |
||||||
|
{ |
||||||
|
$this->mainDirectory = $value; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the name of the main directory |
||||||
|
* @return string Main directory |
||||||
|
*/ |
||||||
|
public function getMainDirectory() |
||||||
|
{ |
||||||
|
return $this->mainDirectory; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Function to upload the individual file chunks |
||||||
|
* @return string JSON object with result of upload |
||||||
|
*/ |
||||||
|
public function uploadFile() |
||||||
|
{ |
||||||
|
//Make sure the total file we're writing to hasn't surpassed the file size limit |
||||||
|
if(file_exists($this->getTempDirectory() . $this->getTempName())) { |
||||||
|
if(filesize($this->getTempDirectory() . $this->getTempName()) > self::MAX_SIZE) { |
||||||
|
$this->abortUpload(); |
||||||
|
return json_encode(array( |
||||||
|
'errorStatus' => 1, |
||||||
|
'errorText' => 'File is too large.' |
||||||
|
)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//Open the raw POST data from php://input |
||||||
|
$fileData = file_get_contents('php://input'); |
||||||
|
|
||||||
|
//Write the actual chunk to the larger file |
||||||
|
$handle = fopen($this->getTempDirectory() . $this->getTempName(), 'a'); |
||||||
|
|
||||||
|
fwrite($handle, $fileData); |
||||||
|
fclose($handle); |
||||||
|
|
||||||
|
return json_encode(array( |
||||||
|
'key' => $this->getTempName(), |
||||||
|
'errorStatus' => 0 |
||||||
|
)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Function for cancelling uploads while they're in-progress; deletes the temp file |
||||||
|
* @return string JSON object with result of deletion |
||||||
|
*/ |
||||||
|
public function abortUpload() |
||||||
|
{ |
||||||
|
if(unlink($this->getTempDirectory() . $this->getTempName())) { |
||||||
|
return json_encode(array('errorStatus' => 0)); |
||||||
|
} |
||||||
|
else { |
||||||
|
|
||||||
|
return json_encode(array( |
||||||
|
'errorStatus' => 1, |
||||||
|
'errorText' => 'Unable to delete temporary file.' |
||||||
|
)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Function to rename and move the finished file |
||||||
|
* @param string $final_name Name to rename the finished upload to |
||||||
|
* @return string JSON object with result of rename |
||||||
|
*/ |
||||||
|
public function finishUpload($finalName) |
||||||
|
{ |
||||||
|
$origin = $_POST['origin']; |
||||||
|
if ($origin == 'document') { |
||||||
|
$tmpFile = $this->getTempDirectory() . $this->getTempName(); |
||||||
|
chmod($tmpFile, '0777'); |
||||||
|
$file = [ |
||||||
|
'name' => $finalName, |
||||||
|
'type' => $_POST['type'], |
||||||
|
'tmp_name' => $tmpFile, |
||||||
|
'error' => 0, |
||||||
|
'size' => $_POST['size'], |
||||||
|
'copy_file' => true, |
||||||
|
]; |
||||||
|
$files = ['file' => $file]; |
||||||
|
$unzip = isset($_POST['unzip']) ? $_POST['unzip'] : null; |
||||||
|
$index = isset($_POST['index_document']) ? $_POST['index_document'] : null; |
||||||
|
DocumentManager::upload_document( |
||||||
|
$files, |
||||||
|
$_POST['curdirpath'], |
||||||
|
$_POST['title'], |
||||||
|
$_POST['comment'], |
||||||
|
$unzip, |
||||||
|
$_POST['if_exists'], |
||||||
|
$index, |
||||||
|
true |
||||||
|
); |
||||||
|
$redirectUrl = api_get_path(WEB_CODE_PATH).'document/document.php?'.api_get_cidreq(); |
||||||
|
if (!empty($_POST['id'])) { |
||||||
|
$redirectUrl .= '&'.http_build_query( |
||||||
|
[ |
||||||
|
'id' => $_POST['id'], |
||||||
|
] |
||||||
|
); |
||||||
|
} |
||||||
|
return json_encode(array('errorStatus' => 0, 'redirect' => $redirectUrl)); |
||||||
|
} else if ($origin == 'learnpath') { |
||||||
|
|
||||||
|
unset($_REQUEST['origin']); |
||||||
|
$redirectUrl = api_get_path(WEB_CODE_PATH).'upload/upload.php?'.api_get_cidreq().'&from=bigUpload&name='.$this->getTempName(); |
||||||
|
return json_encode(array('errorStatus' => 0, 'redirect' => $redirectUrl)); |
||||||
|
|
||||||
|
} else if ($origin == 'work') { |
||||||
|
|
||||||
|
$tmpFile = $this->getTempDirectory() . $this->getTempName(); |
||||||
|
chmod($tmpFile, '0777'); |
||||||
|
$workInfo = get_work_data_by_id($_REQUEST['id']); |
||||||
|
$values = $_REQUEST; |
||||||
|
$course_info = api_get_course_info(); |
||||||
|
$session_id = api_get_session_id(); |
||||||
|
$group_id = api_get_group_id(); |
||||||
|
$user_id = api_get_user_id(); |
||||||
|
$values['contains_file'] = 1; |
||||||
|
$file = [ |
||||||
|
'name' => $finalName, |
||||||
|
'type' => $_POST['type'], |
||||||
|
'tmp_name' => $tmpFile, |
||||||
|
'error' => 0, |
||||||
|
'size' => $_POST['size'], |
||||||
|
'copy_file' => true, |
||||||
|
]; |
||||||
|
|
||||||
|
// Process work |
||||||
|
$result = processWorkForm( |
||||||
|
$workInfo, |
||||||
|
$values, |
||||||
|
$course_info, |
||||||
|
$session_id, |
||||||
|
$group_id, |
||||||
|
$user_id, |
||||||
|
$file, |
||||||
|
api_get_configuration_value('assignment_prevent_duplicate_upload') |
||||||
|
); |
||||||
|
$redirectUrl = api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq(); |
||||||
|
return json_encode(array('errorStatus' => 0, 'redirect' => $redirectUrl)); |
||||||
|
} |
||||||
|
|
||||||
|
return json_encode(array('errorStatus' => 0)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Basic php file upload function, used for unsupported browsers. |
||||||
|
* The output on success/failure is very basic, and it would be best to have these errors return the user to index.html |
||||||
|
* with the errors printed on the form, but that is beyond the scope of this project as it is very application specific. |
||||||
|
* @return string Success or failure of upload |
||||||
|
*/ |
||||||
|
public function postUnsupported() |
||||||
|
{ |
||||||
|
$name = $_FILES['bigUploadFile']['name']; |
||||||
|
$size = $_FILES['bigUploadFile']['size']; |
||||||
|
$tempName = $_FILES['bigUploadFile']['tmp_name']; |
||||||
|
|
||||||
|
if(filesize($tempName) > self::MAX_SIZE) { |
||||||
|
return 'File is too large.'; |
||||||
|
} |
||||||
|
|
||||||
|
if(move_uploaded_file($tempName, $this->getMainDirectory() . $name)) { |
||||||
|
return 'File uploaded.'; |
||||||
|
} |
||||||
|
else { |
||||||
|
return 'There was an error uploading the file'; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//Instantiate the class |
||||||
|
$bigUpload = new BigUpload; |
||||||
|
|
||||||
|
//Set the temporary filename |
||||||
|
$tempName = null; |
||||||
|
if(isset($_GET['key'])) { |
||||||
|
$tempName = $_GET['key']; |
||||||
|
} |
||||||
|
if(isset($_POST['key'])) { |
||||||
|
$tempName = $_POST['key']; |
||||||
|
} |
||||||
|
$bigUpload->setTempName($tempName); |
||||||
|
|
||||||
|
switch($_GET['action']) { |
||||||
|
case 'upload': |
||||||
|
print $bigUpload->uploadFile(); |
||||||
|
break; |
||||||
|
case 'abort': |
||||||
|
print $bigUpload->abortUpload(); |
||||||
|
break; |
||||||
|
case 'finish': |
||||||
|
print $bigUpload->finishUpload($_POST['name']); |
||||||
|
break; |
||||||
|
case 'post-unsupported': |
||||||
|
print $bigUpload->postUnsupported(); |
||||||
|
break; |
||||||
|
} |
||||||
|
?> |
||||||
@ -0,0 +1,336 @@ |
|||||||
|
function bigUpload () { |
||||||
|
|
||||||
|
//These are the main config variables and should be able to take care of most of the customization
|
||||||
|
this.settings = { |
||||||
|
//The id of the file input
|
||||||
|
'inputField': 'bigUploadFile', |
||||||
|
|
||||||
|
//The id of the form with the file upload.
|
||||||
|
//This should be a valid html form (see index.html) so there is a fallback for unsupported browsers
|
||||||
|
'formId': 'bigUploadForm', |
||||||
|
|
||||||
|
//The id of the progress bar
|
||||||
|
//Width of this element will change based on progress
|
||||||
|
//Content of this element will display a percentage
|
||||||
|
//See bigUpload.progressUpdate() to change this code
|
||||||
|
'progressBarField': 'bigUploadProgressBarFilled', |
||||||
|
|
||||||
|
//The id of the time remaining field
|
||||||
|
//Content of this element will display the estimated time remaining for the upload
|
||||||
|
//See bigUpload.progressUpdate() to change this code
|
||||||
|
'timeRemainingField': 'bigUploadTimeRemaining', |
||||||
|
|
||||||
|
//The id of the text response field
|
||||||
|
//Content of this element will display the response from the server on success or error
|
||||||
|
'responseField': 'bigUploadResponse', |
||||||
|
|
||||||
|
//The id of the submit button
|
||||||
|
//This is then changed to become the pause/resume button based on the status of the upload
|
||||||
|
'submitButton': 'bigUploadSubmit', |
||||||
|
|
||||||
|
//Color of the background of the progress bar
|
||||||
|
//This must also be defined in the progressBarField css, but it's used here to reset the color after an error
|
||||||
|
//Default: green
|
||||||
|
'progressBarColor': '#5bb75b', |
||||||
|
|
||||||
|
//Color of the background of the progress bar when an error is triggered
|
||||||
|
//Default: red
|
||||||
|
'progressBarColorError': '#da4f49', |
||||||
|
|
||||||
|
//Path to the php script for handling the uploads
|
||||||
|
'scriptPath': 'inc/bigUpload.php', |
||||||
|
|
||||||
|
//Size of chunks to upload (in bytes)
|
||||||
|
//Default: 1MB
|
||||||
|
'chunkSize': 1000000, |
||||||
|
|
||||||
|
//Max file size allowed
|
||||||
|
//Default: 2GB
|
||||||
|
'maxFileSize': 2147483648 |
||||||
|
}; |
||||||
|
|
||||||
|
//Upload specific variables
|
||||||
|
this.uploadData = { |
||||||
|
'uploadStarted': false, |
||||||
|
'file': false, |
||||||
|
'numberOfChunks': 0, |
||||||
|
'aborted': false, |
||||||
|
'paused': false, |
||||||
|
'pauseChunk': 0, |
||||||
|
'key': 0, |
||||||
|
'timeStart': 0, |
||||||
|
'totalTime': 0 |
||||||
|
}; |
||||||
|
|
||||||
|
parent = this; |
||||||
|
|
||||||
|
//Quick function for accessing objects
|
||||||
|
this.$ = function(id) { |
||||||
|
return document.getElementById(id); |
||||||
|
}; |
||||||
|
|
||||||
|
//Resets all the upload specific data before a new upload
|
||||||
|
this.resetKey = function() { |
||||||
|
this.uploadData = { |
||||||
|
'uploadStarted': false, |
||||||
|
'file': false, |
||||||
|
'numberOfChunks': 0, |
||||||
|
'aborted': false, |
||||||
|
'paused': false, |
||||||
|
'pauseChunk': 0, |
||||||
|
'key': 0, |
||||||
|
'timeStart': 0, |
||||||
|
'totalTime': 0 |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
//Inital method called
|
||||||
|
//Determines whether to begin/pause/resume an upload based on whether or not one is already in progress
|
||||||
|
this.fire = function() { |
||||||
|
if(this.uploadData.uploadStarted === true && this.uploadData.paused === false) { |
||||||
|
this.pauseUpload(); |
||||||
|
} |
||||||
|
else if(this.uploadData.uploadStarted === true && this.uploadData.paused === true) { |
||||||
|
this.resumeUpload(); |
||||||
|
} |
||||||
|
else { |
||||||
|
this.processFiles(); |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
//Initial upload method
|
||||||
|
//Pulls the size of the file being uploaded and calculated the number of chunks, then calls the recursive upload method
|
||||||
|
this.processFiles = function() { |
||||||
|
|
||||||
|
//If the user is using an unsupported browser, the form just submits as a regular form
|
||||||
|
if(!Blob.prototype.slice) { |
||||||
|
this.$(this.settings.formId).submit(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//Reset the upload-specific variables
|
||||||
|
this.resetKey(); |
||||||
|
this.uploadData.uploadStarted = true; |
||||||
|
|
||||||
|
//Some HTML tidying
|
||||||
|
//Reset the background color of the progress bar in case it was changed by any earlier errors
|
||||||
|
//Change the Upload button to a Pause button
|
||||||
|
this.$(this.settings.progressBarField).style.backgroundColor = this.settings.progressBarColor; |
||||||
|
this.$(this.settings.responseField).textContent = ''; |
||||||
|
this.$(this.settings.submitButton).value = 'Pause'; |
||||||
|
|
||||||
|
//Alias the file input object to this.uploadData
|
||||||
|
this.uploadData.file = this.$(this.settings.inputField).files[0]; |
||||||
|
|
||||||
|
//Check the filesize. Obviously this is not very secure, so it has another check in inc/bigUpload.php
|
||||||
|
//But this should be good enough to catch any immediate errors
|
||||||
|
var fileSize = this.uploadData.file.size; |
||||||
|
if(fileSize > this.settings.maxFileSize) { |
||||||
|
this.printResponse('The file you have chosen is too large.', true); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//Calculate the total number of file chunks
|
||||||
|
this.uploadData.numberOfChunks = Math.ceil(fileSize / this.settings.chunkSize); |
||||||
|
|
||||||
|
//Start the upload
|
||||||
|
this.sendFile(0); |
||||||
|
}; |
||||||
|
|
||||||
|
//Main upload method
|
||||||
|
this.sendFile = function (chunk) { |
||||||
|
|
||||||
|
//Set the time for the beginning of the upload, used for calculating time remaining
|
||||||
|
this.uploadData.timeStart = new Date().getTime(); |
||||||
|
|
||||||
|
//Check if the upload has been cancelled by the user
|
||||||
|
if(this.uploadData.aborted === true) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//Check if the upload has been paused by the user
|
||||||
|
if(this.uploadData.paused === true) { |
||||||
|
this.uploadData.pauseChunk = chunk; |
||||||
|
this.printResponse('Upload paused.', false); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//Set the byte to start uploading from and the byte to end uploading at
|
||||||
|
var start = chunk * this.settings.chunkSize; |
||||||
|
var stop = start + this.settings.chunkSize; |
||||||
|
|
||||||
|
//Initialize a new FileReader object
|
||||||
|
var reader = new FileReader(); |
||||||
|
|
||||||
|
reader.onloadend = function(evt) { |
||||||
|
|
||||||
|
//Build the AJAX request
|
||||||
|
//
|
||||||
|
//this.uploadData.key is the temporary filename
|
||||||
|
//If the server sees it as 0 it will generate a new filename and pass it back in the JSON object
|
||||||
|
//this.uploadData.key is then populated with the filename to use for subsequent requests
|
||||||
|
//When this method sends a valid filename (i.e. key != 0), the server will just append the data being sent to that file.
|
||||||
|
xhr = new XMLHttpRequest(); |
||||||
|
xhr.open("POST", parent.settings.scriptPath + '?action=upload&origin=' + parent.settings.origin+'&key=' + parent.uploadData.key, true); |
||||||
|
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); |
||||||
|
|
||||||
|
xhr.onreadystatechange = function() { |
||||||
|
if(xhr.readyState == 4) { |
||||||
|
var response = JSON.parse(xhr.response); |
||||||
|
|
||||||
|
//If there's an error, call the error method and break the loop
|
||||||
|
if(response.errorStatus !== 0 || xhr.status != 200) { |
||||||
|
parent.printResponse(response.errorText, true); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//If it's the first chunk, set this.uploadData.key to the server response (see above)
|
||||||
|
if(chunk === 0 || parent.uploadData.key === 0) { |
||||||
|
parent.uploadData.key = response.key; |
||||||
|
} |
||||||
|
|
||||||
|
//If the file isn't done uploading, update the progress bar and run this.sendFile again for the next chunk
|
||||||
|
if(chunk < parent.uploadData.numberOfChunks) { |
||||||
|
parent.progressUpdate(chunk + 1); |
||||||
|
parent.sendFile(chunk + 1); |
||||||
|
} |
||||||
|
//If the file is complete uploaded, instantiate the finalizing method
|
||||||
|
else { |
||||||
|
parent.sendFileData(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
//Send the file chunk
|
||||||
|
xhr.send(blob); |
||||||
|
}; |
||||||
|
|
||||||
|
//Slice the file into the desired chunk
|
||||||
|
//This is the core of the script. Everything else is just fluff.
|
||||||
|
var blob = this.uploadData.file.slice(start, stop); |
||||||
|
reader.readAsBinaryString(blob); |
||||||
|
}; |
||||||
|
|
||||||
|
//This method is for whatever housekeeping work needs to be completed after the file is finished uploading.
|
||||||
|
//As it's setup now, it passes along the original filename to the server and the server renames the file and removes it form the temp directory.
|
||||||
|
//This function could also pass things like this.uploadData.file.type for the mime-type (although it would be more accurate to use php for that)
|
||||||
|
//Or it could pass along user information or something like that, depending on the context of the application.
|
||||||
|
this.sendFileData = function() { |
||||||
|
var data = 'key=' + this.uploadData.key + '&name=' + this.uploadData.file.name + '&type=' + this.uploadData.file.type + '&size=' + this.uploadData.file.size + '&origin=' + parent.settings.origin + '&' + parent.settings.formParams; |
||||||
|
xhr = new XMLHttpRequest(); |
||||||
|
xhr.open("POST", parent.settings.scriptPath + '?action=finish', true); |
||||||
|
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); |
||||||
|
|
||||||
|
xhr.onreadystatechange = function() { |
||||||
|
if(xhr.readyState == 4) { |
||||||
|
var response = JSON.parse(xhr.response); |
||||||
|
|
||||||
|
//If there's an error, call the error method
|
||||||
|
if(response.errorStatus !== 0 || xhr.status != 200) { |
||||||
|
parent.printResponse(response.errorText, true); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//Reset the upload-specific data so we can process another upload
|
||||||
|
parent.resetKey(); |
||||||
|
|
||||||
|
//Change the submit button text so it's ready for another upload and spit out a sucess message
|
||||||
|
parent.$(parent.settings.submitButton).value = 'Start Upload'; |
||||||
|
parent.printResponse('File uploaded successfully.', false); |
||||||
|
|
||||||
|
if (response.redirect) { |
||||||
|
location.href = response.redirect; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
//Send the reques
|
||||||
|
xhr.send(data); |
||||||
|
}; |
||||||
|
|
||||||
|
//This method cancels the upload of a file.
|
||||||
|
//It sets this.uploadData.aborted to true, which stops the recursive upload script.
|
||||||
|
//The server then removes the incomplete file from the temp directory, and the html displays an error message.
|
||||||
|
this.abortFileUpload = function() { |
||||||
|
this.uploadData.aborted = true; |
||||||
|
var data = 'key=' + this.uploadData.key; |
||||||
|
xhr = new XMLHttpRequest(); |
||||||
|
xhr.open("POST", this.settings.scriptPath + '?action=abort', true); |
||||||
|
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); |
||||||
|
|
||||||
|
xhr.onreadystatechange = function() { |
||||||
|
if(xhr.readyState == 4) { |
||||||
|
var response = JSON.parse(xhr.response); |
||||||
|
|
||||||
|
//If there's an error, call the error method.
|
||||||
|
if(response.errorStatus !== 0 || xhr.status != 200) { |
||||||
|
parent.printResponse(response.errorText, true); |
||||||
|
return; |
||||||
|
} |
||||||
|
parent.printResponse('File upload was cancelled.', true); |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
//Send the request
|
||||||
|
xhr.send(data); |
||||||
|
}; |
||||||
|
|
||||||
|
//Pause the upload
|
||||||
|
//Sets this.uploadData.paused to true, which breaks the upload loop.
|
||||||
|
//The current chunk is still stored in this.uploadData.pauseChunk, so the upload can later be resumed.
|
||||||
|
//In a production environment, you might want to have a cron job to clean up files that have been paused and never resumed,
|
||||||
|
//because this method won't delete the file from the temp directory if the user pauses and then leaves the page.
|
||||||
|
this.pauseUpload = function() { |
||||||
|
this.uploadData.paused = true; |
||||||
|
this.printResponse('', false); |
||||||
|
this.$(this.settings.submitButton).value = 'Resume'; |
||||||
|
}; |
||||||
|
|
||||||
|
//Resume the upload
|
||||||
|
//Undoes the doings of this.pauseUpload and then re-enters the loop at the last chunk uploaded
|
||||||
|
this.resumeUpload = function() { |
||||||
|
this.uploadData.paused = false; |
||||||
|
this.$(this.settings.submitButton).value = 'Pause'; |
||||||
|
this.sendFile(this.uploadData.pauseChunk); |
||||||
|
}; |
||||||
|
|
||||||
|
//This method updates a simple progress bar by calculating the percentage of chunks uploaded.
|
||||||
|
//Also includes a method to calculate the time remaining by taking the average time to upload individual chunks
|
||||||
|
//and multiplying it by the number of chunks remaining.
|
||||||
|
this.progressUpdate = function(progress) { |
||||||
|
|
||||||
|
var percent = Math.ceil((progress / this.uploadData.numberOfChunks) * 100); |
||||||
|
this.$(this.settings.progressBarField).style.width = percent + '%'; |
||||||
|
this.$(this.settings.progressBarField).textContent = percent + '%'; |
||||||
|
|
||||||
|
//Calculate the estimated time remaining
|
||||||
|
//Only run this every five chunks, otherwise the time remaining jumps all over the place (see: http://xkcd.com/612/)
|
||||||
|
if(progress % 5 === 0) { |
||||||
|
|
||||||
|
//Calculate the total time for all of the chunks uploaded so far
|
||||||
|
this.uploadData.totalTime += (new Date().getTime() - this.uploadData.timeStart); |
||||||
|
console.log(this.uploadData.totalTime); |
||||||
|
|
||||||
|
//Estimate the time remaining by finding the average time per chunk upload and
|
||||||
|
//multiplying it by the number of chunks remaining, then convert into seconds
|
||||||
|
var timeLeft = Math.ceil((this.uploadData.totalTime / progress) * (this.uploadData.numberOfChunks - progress) / 100); |
||||||
|
|
||||||
|
//Update this.settings.timeRemainingField with the estimated time remaining
|
||||||
|
this.$(this.settings.timeRemainingField).textContent = timeLeft + ' seconds remaining'; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
//Simple response/error handler
|
||||||
|
this.printResponse = function(responseText, error) { |
||||||
|
this.$(this.settings.responseField).textContent = responseText; |
||||||
|
this.$(this.settings.timeRemainingField).textContent = ''; |
||||||
|
if(error === true) { |
||||||
|
this.$(this.settings.progressBarField).style.backgroundColor = this.settings.progressBarColorError; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
@ -0,0 +1,350 @@ |
|||||||
|
function bigUpload () { |
||||||
|
|
||||||
|
//These are the main config variables and should be able to take care of most of the customization
|
||||||
|
this.settings = { |
||||||
|
//The id of the file input
|
||||||
|
'inputField': 'bigUploadFile', |
||||||
|
|
||||||
|
//The id of the form with the file upload.
|
||||||
|
//This should be a valid html form (see index.html) so there is a fallback for unsupported browsers
|
||||||
|
'formId': 'bigUploadForm', |
||||||
|
|
||||||
|
//The id of the progress bar
|
||||||
|
//Width of this element will change based on progress
|
||||||
|
//Content of this element will display a percentage
|
||||||
|
//See bigUpload.progressUpdate() to change this code
|
||||||
|
'progressBarField': 'bigUploadProgressBarFilled', |
||||||
|
|
||||||
|
//The id of the time remaining field
|
||||||
|
//Content of this element will display the estimated time remaining for the upload
|
||||||
|
//See bigUpload.progressUpdate() to change this code
|
||||||
|
'timeRemainingField': 'bigUploadTimeRemaining', |
||||||
|
|
||||||
|
//The id of the text response field
|
||||||
|
//Content of this element will display the response from the server on success or error
|
||||||
|
'responseField': 'bigUploadResponse', |
||||||
|
|
||||||
|
//The id of the submit button
|
||||||
|
//This is then changed to become the pause/resume button based on the status of the upload
|
||||||
|
'submitButton': 'bigUploadSubmit', |
||||||
|
|
||||||
|
//Color of the background of the progress bar
|
||||||
|
//This must also be defined in the progressBarField css, but it's used here to reset the color after an error
|
||||||
|
//Default: green
|
||||||
|
'progressBarColor': '#5bb75b', |
||||||
|
|
||||||
|
//Color of the background of the progress bar when an error is triggered
|
||||||
|
//Default: red
|
||||||
|
'progressBarColorError': '#da4f49', |
||||||
|
|
||||||
|
//Path to the php script for handling the uploads
|
||||||
|
'scriptPath': 'inc/big-upload.php', |
||||||
|
|
||||||
|
//Additional URL variables to be passed to the script path
|
||||||
|
//ex: &foo=bar
|
||||||
|
'scriptPathParams': '', |
||||||
|
|
||||||
|
//Size of chunks to upload (in bytes)
|
||||||
|
//Default: 1MB defaut : 1000000
|
||||||
|
'chunkSize': 50000, |
||||||
|
|
||||||
|
//Max file size allowed
|
||||||
|
//Default: 2GB defaut : 2147483648
|
||||||
|
'maxFileSize': 414748364 |
||||||
|
}; |
||||||
|
this.scormid = ''; |
||||||
|
|
||||||
|
//Upload specific variables
|
||||||
|
this.uploadData = { |
||||||
|
'uploadStarted': false, |
||||||
|
'file': false, |
||||||
|
'numberOfChunks': 0, |
||||||
|
'aborted': false, |
||||||
|
'paused': false, |
||||||
|
'pauseChunk': 0, |
||||||
|
'key': 0, |
||||||
|
'timeStart': 0, |
||||||
|
'totalTime': 0 |
||||||
|
}; |
||||||
|
|
||||||
|
//Success callback
|
||||||
|
this.success = function(response) { |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
parent = this; |
||||||
|
|
||||||
|
//Quick function for accessing objects
|
||||||
|
this.$ = function(id) { |
||||||
|
return document.getElementById(id); |
||||||
|
}; |
||||||
|
|
||||||
|
//Resets all the upload specific data before a new upload
|
||||||
|
this.resetKey = function() { |
||||||
|
this.uploadData = { |
||||||
|
'uploadStarted': false, |
||||||
|
'file': false, |
||||||
|
'numberOfChunks': 0, |
||||||
|
'aborted': false, |
||||||
|
'paused': false, |
||||||
|
'pauseChunk': 0, |
||||||
|
'key': 0, |
||||||
|
'timeStart': 0, |
||||||
|
'totalTime': 0 |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
//Inital method called
|
||||||
|
//Determines whether to begin/pause/resume an upload based on whether or not one is already in progress
|
||||||
|
this.fire = function() { |
||||||
|
if(this.uploadData.uploadStarted === true && this.uploadData.paused === false) { |
||||||
|
this.pauseUpload(); |
||||||
|
} |
||||||
|
else if(this.uploadData.uploadStarted === true && this.uploadData.paused === true) { |
||||||
|
this.resumeUpload(); |
||||||
|
} |
||||||
|
else { |
||||||
|
this.processFiles(); |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
//Initial upload method
|
||||||
|
//Pulls the size of the file being uploaded and calculated the number of chunks, then calls the recursive upload method
|
||||||
|
this.processFiles = function() { |
||||||
|
|
||||||
|
//If the user is using an unsupported browser, the form just submits as a regular form
|
||||||
|
if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { |
||||||
|
this.$(this.settings.formId).submit(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//Reset the upload-specific variables
|
||||||
|
this.resetKey(); |
||||||
|
this.uploadData.uploadStarted = true; |
||||||
|
|
||||||
|
//Some HTML tidying
|
||||||
|
//Reset the background color of the progress bar in case it was changed by any earlier errors
|
||||||
|
//Change the Upload button to a Pause button
|
||||||
|
this.$(this.settings.progressBarField).style.backgroundColor = this.settings.progressBarColor; |
||||||
|
this.$(this.settings.responseField).textContent = 'Envoi...'; |
||||||
|
this.$(this.settings.submitButton).value = 'Pause'; |
||||||
|
|
||||||
|
//Alias the file input object to this.uploadData
|
||||||
|
this.uploadData.file = this.$(this.settings.inputField).files[0]; |
||||||
|
|
||||||
|
//Check the filesize. Obviously this is not very secure, so it has another check in inc/bigUpload.php
|
||||||
|
//But this should be good enough to catch any immediate errors
|
||||||
|
var fileSize = this.uploadData.file.size; |
||||||
|
if(fileSize > this.settings.maxFileSize) { |
||||||
|
this.printResponse('Votre fichier est trop gros.', true); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//Calculate the total number of file chunks
|
||||||
|
this.uploadData.numberOfChunks = Math.ceil(fileSize / this.settings.chunkSize); |
||||||
|
|
||||||
|
//Start the upload
|
||||||
|
this.sendFile(0); |
||||||
|
}; |
||||||
|
|
||||||
|
//Main upload method
|
||||||
|
this.sendFile = function (chunk) { |
||||||
|
|
||||||
|
//Set the time for the beginning of the upload, used for calculating time remaining
|
||||||
|
this.uploadData.timeStart = new Date().getTime(); |
||||||
|
|
||||||
|
//Check if the upload has been cancelled by the user
|
||||||
|
if(this.uploadData.aborted === true) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//Check if the upload has been paused by the user
|
||||||
|
if(this.uploadData.paused === true) { |
||||||
|
this.uploadData.pauseChunk = chunk; |
||||||
|
this.printResponse('En pause.', false); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//Set the byte to start uploading from and the byte to end uploading at
|
||||||
|
var start = chunk * this.settings.chunkSize; |
||||||
|
var stop = start + this.settings.chunkSize; |
||||||
|
|
||||||
|
//Initialize a new FileReader object
|
||||||
|
var reader = new FileReader(); |
||||||
|
|
||||||
|
reader.onloadend = function(evt) { |
||||||
|
|
||||||
|
//Build the AJAX request
|
||||||
|
//
|
||||||
|
//this.uploadData.key is the temporary filename
|
||||||
|
//If the server sees it as 0 it will generate a new filename and pass it back in the JSON object
|
||||||
|
//this.uploadData.key is then populated with the filename to use for subsequent requests
|
||||||
|
//When this method sends a valid filename (i.e. key != 0), the server will just append the data being sent to that file.
|
||||||
|
xhr = new XMLHttpRequest(); |
||||||
|
xhr.open("POST", parent.settings.scriptPath + '?action=upload&key=' + parent.uploadData.key + parent.settings.scriptPathParams, true); |
||||||
|
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); |
||||||
|
|
||||||
|
xhr.onreadystatechange = function() { |
||||||
|
if(xhr.readyState == 4) { |
||||||
|
|
||||||
|
var response = JSON.parse(xhr.response); |
||||||
|
|
||||||
|
//If there's an error, call the error method and break the loop
|
||||||
|
if(response.errorStatus !== 0 || xhr.status != 200) { |
||||||
|
parent.printResponse(response.errorText, true); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//If it's the first chunk, set this.uploadData.key to the server response (see above)
|
||||||
|
if(chunk === 0 || parent.uploadData.key === 0) { |
||||||
|
parent.uploadData.key = response.key; |
||||||
|
} |
||||||
|
|
||||||
|
//If the file isn't done uploading, update the progress bar and run this.sendFile again for the next chunk
|
||||||
|
if(chunk < parent.uploadData.numberOfChunks) { |
||||||
|
parent.progressUpdate(chunk + 1); |
||||||
|
parent.sendFile(chunk + 1); |
||||||
|
} |
||||||
|
//If the file is complete uploaded, instantiate the finalizing method
|
||||||
|
else { |
||||||
|
parent.sendFileData(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
//Send the file chunk
|
||||||
|
xhr.send(blob); |
||||||
|
}; |
||||||
|
|
||||||
|
//Slice the file into the desired chunk
|
||||||
|
//This is the core of the script. Everything else is just fluff.
|
||||||
|
var blob = this.uploadData.file.slice(start, stop); |
||||||
|
reader.readAsBinaryString(blob); |
||||||
|
}; |
||||||
|
|
||||||
|
//This method is for whatever housekeeping work needs to be completed after the file is finished uploading.
|
||||||
|
//As it's setup now, it passes along the original filename to the server and the server renames the file and removes it form the temp directory.
|
||||||
|
//This function could also pass things like this.uploadData.file.type for the mime-type (although it would be more accurate to use php for that)
|
||||||
|
//Or it could pass along user information or something like that, depending on the context of the application.
|
||||||
|
this.sendFileData = function() { |
||||||
|
var data = 'key=' + this.uploadData.key + '&name=' + this.uploadData.file.name + '&scormid=' + this.scormid; |
||||||
|
xhr = new XMLHttpRequest(); |
||||||
|
xhr.open("POST", parent.settings.scriptPath + '?action=finish', true); |
||||||
|
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); |
||||||
|
|
||||||
|
xhr.onreadystatechange = function() { |
||||||
|
if(xhr.readyState == 4) { |
||||||
|
|
||||||
|
var response = JSON.parse(xhr.response); |
||||||
|
|
||||||
|
//If there's an error, call the error method
|
||||||
|
if(response.errorStatus !== 0 || xhr.status != 200) { |
||||||
|
parent.printResponse(response.errorText, true); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//Reset the upload-specific data so we can process another upload
|
||||||
|
parent.resetKey(); |
||||||
|
|
||||||
|
//Change the submit button text so it's ready for another upload and spit out a sucess message
|
||||||
|
parent.$(parent.settings.submitButton).value = 'Start Upload'; |
||||||
|
//response.errorText = 'VOTRE CODE : ' + this.scormid;
|
||||||
|
var mess = 'CODEisOK'; |
||||||
|
parent.printResponse(mess, false); |
||||||
|
|
||||||
|
$('#finalNameSrc').html(response.finalName); |
||||||
|
|
||||||
|
parent.success(response); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
//Send the reques
|
||||||
|
xhr.send(data); |
||||||
|
}; |
||||||
|
|
||||||
|
//This method cancels the upload of a file.
|
||||||
|
//It sets this.uploadData.aborted to true, which stops the recursive upload script.
|
||||||
|
//The server then removes the incomplete file from the temp directory, and the html displays an error message.
|
||||||
|
this.abortFileUpload = function() { |
||||||
|
this.uploadData.aborted = true; |
||||||
|
var data = 'key=' + this.uploadData.key; |
||||||
|
xhr = new XMLHttpRequest(); |
||||||
|
xhr.open("POST", this.settings.scriptPath + '?action=abort', true); |
||||||
|
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); |
||||||
|
|
||||||
|
xhr.onreadystatechange = function() { |
||||||
|
if(xhr.readyState == 4) { |
||||||
|
var response = JSON.parse(xhr.response); |
||||||
|
|
||||||
|
//If there's an error, call the error method.
|
||||||
|
if(response.errorStatus !== 0 || xhr.status != 200) { |
||||||
|
parent.printResponse(response.errorText, true); |
||||||
|
return; |
||||||
|
} |
||||||
|
parent.printResponse('File upload was cancelled.', true); |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
//Send the request
|
||||||
|
xhr.send(data); |
||||||
|
}; |
||||||
|
|
||||||
|
//Pause the upload
|
||||||
|
//Sets this.uploadData.paused to true, which breaks the upload loop.
|
||||||
|
//The current chunk is still stored in this.uploadData.pauseChunk, so the upload can later be resumed.
|
||||||
|
//In a production environment, you might want to have a cron job to clean up files that have been paused and never resumed,
|
||||||
|
//because this method won't delete the file from the temp directory if the user pauses and then leaves the page.
|
||||||
|
this.pauseUpload = function() { |
||||||
|
this.uploadData.paused = true; |
||||||
|
this.printResponse('', false); |
||||||
|
this.$(this.settings.submitButton).value = 'Resume'; |
||||||
|
}; |
||||||
|
|
||||||
|
//Resume the upload
|
||||||
|
//Undoes the doings of this.pauseUpload and then re-enters the loop at the last chunk uploaded
|
||||||
|
this.resumeUpload = function() { |
||||||
|
this.uploadData.paused = false; |
||||||
|
this.$(this.settings.submitButton).value = 'Pause'; |
||||||
|
this.sendFile(this.uploadData.pauseChunk); |
||||||
|
}; |
||||||
|
|
||||||
|
//This method updates a simple progress bar by calculating the percentage of chunks uploaded.
|
||||||
|
//Also includes a method to calculate the time remaining by taking the average time to upload individual chunks
|
||||||
|
//and multiplying it by the number of chunks remaining.
|
||||||
|
this.progressUpdate = function(progress) { |
||||||
|
|
||||||
|
var percent = Math.ceil((progress / this.uploadData.numberOfChunks) * 100); |
||||||
|
this.$(this.settings.progressBarField).style.width = percent + '%'; |
||||||
|
this.$(this.settings.progressBarField).textContent = percent + '%'; |
||||||
|
|
||||||
|
//Calculate the estimated time remaining
|
||||||
|
//Only run this every five chunks, otherwise the time remaining jumps all over the place (see: http://xkcd.com/612/)
|
||||||
|
if(progress % 5 === 0) { |
||||||
|
|
||||||
|
//Calculate the total time for all of the chunks uploaded so far
|
||||||
|
this.uploadData.totalTime += (new Date().getTime() - this.uploadData.timeStart); |
||||||
|
console.log(this.uploadData.totalTime); |
||||||
|
|
||||||
|
//Estimate the time remaining by finding the average time per chunk upload and
|
||||||
|
//multiplying it by the number of chunks remaining, then convert into seconds
|
||||||
|
var timeLeft = Math.ceil((this.uploadData.totalTime / progress) * (this.uploadData.numberOfChunks - progress) / 100); |
||||||
|
console.log(Math.ceil(((this.uploadData.totalTime / progress) * this.settings.chunkSize) / 1024) + 'kb/s'); |
||||||
|
|
||||||
|
//Update this.settings.timeRemainingField with the estimated time remaining
|
||||||
|
this.$(this.settings.timeRemainingField).textContent = timeLeft + ' seconds remaining'; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
//Simple response/error handler
|
||||||
|
this.printResponse = function(responseText, error) { |
||||||
|
this.$(this.settings.responseField).textContent = responseText; |
||||||
|
this.$(this.settings.timeRemainingField).textContent = ''; |
||||||
|
if(error === true) { |
||||||
|
this.$(this.settings.progressBarField).style.backgroundColor = this.settings.progressBarColorError; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
@ -0,0 +1,55 @@ |
|||||||
|
|
||||||
|
bigUpload = new bigUpload(); |
||||||
|
bigUpload.scormid = document.getElementById("scormid").value; |
||||||
|
|
||||||
|
function upload() { |
||||||
|
bigUpload.fire(); |
||||||
|
keepProgress(); |
||||||
|
$("#bigUploadSubmit").css('display','none'); |
||||||
|
$(".bigUploadAbort").css('display',''); |
||||||
|
} |
||||||
|
|
||||||
|
function abort() { |
||||||
|
bigUpload.abortFileUpload(); |
||||||
|
$("#bigUploadSubmit").css('display',''); |
||||||
|
$(".bigUploadAbort").css('display','none'); |
||||||
|
} |
||||||
|
|
||||||
|
var refreshCount = 0; |
||||||
|
var refreshpProgress = 1; |
||||||
|
|
||||||
|
function keepProgress(){ |
||||||
|
$("#bigUploadProgressBarFilled").html(refreshpProgress + '%'); |
||||||
|
$("#bigUploadProgressBarFilled").css("width",refreshpProgress + '%'); |
||||||
|
refreshpProgress++; |
||||||
|
if(refreshpProgress>91){ |
||||||
|
refreshpProgress = 80; |
||||||
|
} |
||||||
|
setTimeout('keepProgress()',2000); |
||||||
|
} |
||||||
|
|
||||||
|
function keepAlive(){ |
||||||
|
var kp = document.getElementById('kp'); |
||||||
|
kp.src = kp.src; |
||||||
|
setTimeout('keepAlive()',20000); |
||||||
|
refreshCount++; |
||||||
|
} |
||||||
|
keepAlive(); |
||||||
|
|
||||||
|
function controlAlive(){ |
||||||
|
|
||||||
|
var kp = document.getElementById('bigUploadResponse').innerHTML; |
||||||
|
if (kp.indexOf('CODEisOK')!=-1) { |
||||||
|
var nameSrc = document.getElementById('finalNameSrc').innerHTML; |
||||||
|
if (nameSrc!='') { |
||||||
|
document.getElementById('run').style.display = 'none'; |
||||||
|
document.getElementById('see').style.display = ''; |
||||||
|
var linkact = document.getElementById('linkact').innerHTML; |
||||||
|
window.location = linkact + "&namesrc=" + nameSrc; |
||||||
|
} |
||||||
|
} else { |
||||||
|
setTimeout('controlAlive()',250);
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
controlAlive(); |
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,30 @@ |
|||||||
|
|
||||||
|
function addLinkBigUpload(){ |
||||||
|
|
||||||
|
if (!document.getElementById("file_user_file_bu")){ |
||||||
|
|
||||||
|
var ludiiconplus = _p['web_plugin'] + 'chamilo_upload_large/css/import_scorm.png'; |
||||||
|
|
||||||
|
var lbu = $('#linkbu').html(); |
||||||
|
|
||||||
|
var h = '<a id="file_user_file_bu" href="'+lbu +'" '; |
||||||
|
h += ' style="cursor:pointer;" '; |
||||||
|
h += ' alt="large files" title="large files">'; |
||||||
|
h += '<img id="studioeltools" src="'+ ludiiconplus + '" '; |
||||||
|
h += ' alt="large files" title="large files" style="cursor:pointer;" /> '; |
||||||
|
h += '</a>'; |
||||||
|
|
||||||
|
var num = $("#toolbar-upload").length; |
||||||
|
if(num==1){ |
||||||
|
$('#toolbar-upload').find("div:first-child").find("div:first-child").append(h); |
||||||
|
} else { |
||||||
|
$('.actions').append(h); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
setTimeout(function(){ |
||||||
|
addLinkBigUpload(); |
||||||
|
},300); |
||||||
Loading…
Reference in new issue