').attr('id', fileId).data('fileId', fileId);
if(me.options.upload.view === 'sidebar') {
var $td = $('').appendTo($row);
var $table = $('').appendTo($td);
$('').addClass('filename').html(fileObject.name).appendTo($(' | ').appendTo($table)).before('Dateiname: | ');
$('').addClass('filesize').html(fileSize).appendTo($(' | ').appendTo($table)).before('Größe: | ');
$('').addClass('fileprogress').html($progressBar).appendTo($(' | ').appendTo($table)).before('Fortschritt: | ');
var $statusInfo = $('').addClass('filestatus').html('Bereit zum Hochladen').appendTo($(' | ').appendTo($table)).before('Status: | ');
var $actionsCell = $('').addClass('fileaction').appendTo($(' | ').data('fileId', fileId).appendTo($table));
}
else {
$('').addClass('filename').html(fileObject.name).appendTo($row);
$(' | ').addClass('filesize').html(fileSize).appendTo($row);
$(' | ').addClass('fileprogress').html($progressBar).appendTo($row);
var $statusInfo = $(' | ').addClass('filestatus').html('Bereit zum Hochladen').appendTo($row);
var $actionsCell = $(' | ').addClass('fileaction').appendTo($row);
}
$actionsCell.append($removeFileButton);
$actionsCell.append($uploadFileButton);
$row.appendTo(me.storage.$filesList);
uploadObject.elements.$progressBar = $progressBar;
uploadObject.elements.$statusInfo = $statusInfo;
uploadObject.elements.$actionCell = $actionsCell;
me.storage.uploads[fileId] = uploadObject;
me.storage.$filesContainer.show();
me.storage.$filesContainer.find('table').show();
},
/**
* @param {String} uploadId
*/
removeFile: function (uploadId) {
var $row = $('#' + uploadId);
if ($row.length === 0) {
alert('Can not remove file upload. Element "#' + uploadId + '" not found.');
return;
}
// Datei existiert nicht (mehr?)
if (!me.storage.uploads.hasOwnProperty(uploadId)) {
return;
}
// Upload läuft gerade
if (me.storage.uploads[uploadId].status === STATUS.UPLOADING) {
return; // @todo Laufenden Upload abbrechen
}
// Tabellenzeile entfernen
$row.remove();
delete me.storage.uploads[uploadId];
},
/**
* @ŧodo Upload starten
*
* @param {String} uploadId
*/
startUpload: function (uploadId) {
if (me.storage.uploads.hasOwnProperty(uploadId)) {
var upload = me.storage.uploads[uploadId];
me.startSingleUpload(upload);
}
},
/**
* @param {Object} upload Einzelner Wert aus me.storage.waiting
*/
startSingleUpload: function (upload) {
if (upload === null) {
return;
}
var fileId = upload.id;
if (typeof fileId === 'undefined') {
throw 'Upload fehlgeschlagen. Unique-ID is missing.';
}
var $tableRow = $('#' + fileId);
var $statusCell = $tableRow.find('.filestatus');
$statusCell.html('Bitte warten...');
upload.elements.$progressBar.val(0);
me.uploadFileChunk(upload, 0);
},
/**
* @param {object} upload
* @param {number} start
*/
uploadFileChunk: function (upload, start) {
var chunkSize = me.options.chunkSize;
// ChunkSize kleiner 10KB macht keinen Sinn; Upload verhindern
if (chunkSize < 10240) {
me.displayUploadError(upload.id, 'ChunkSize ist zu gering (<= 10KB). Upload nicht möglich.');
return;
}
// Im allerersten Upload die ChunkSize auf 100KB stellen
// Server schickt in seiner Antwort das PHP-Upload-Limit mit
if (start === 0) {
chunkSize = 102400; // 102400 = 100KB
}
var offset = start + chunkSize + 1;
var chunkBlob = upload.file.slice(start, offset);
var fileId = upload.id;
if (typeof fileId === 'undefined') {
throw 'Upload fehlgeschlagen. Unique-ID is missing.';
}
upload.status = STATUS.UPLOADING;
upload.elements.$statusInfo.html('Hochladen...');
upload.elements.$actionCell.html('');
// Datei-Inhalt fertig eingelesen => Upload starten
upload.reader.onloadend = function (event) {
if (event.target.readyState !== FileReader.DONE) {
return;
}
var ajaxData = me.options.upload.formData;
ajaxData.file_data = event.target.result;
ajaxData.file_name = upload.file.name;
ajaxData.file_type = upload.file.type;
ajaxData.file_size = upload.file.size;
ajaxData.file_id = upload.id;
ajaxData.file_offset = start;
upload.xhr = $.ajax({
url: me.options.upload.url,
type: 'POST',
dataType: 'json',
cache: false,
data: ajaxData,
error: function (jqXHR, textStatus, errorThrown) {
var errorMessage = 'Unbekannter Fehler #21: ' + errorThrown;
// User hat "Abbrechen" geklickt
if (textStatus === 'abort') {
errorMessage = 'Upload abgebrochen';
}
// PHP-Skript hat Fehler geliefert (z.b. 404)
if (textStatus === 'error') {
errorMessage = 'Unbekannter Server-Fehler';
}
// PHP-Skript liefer JSON-Error-Response
if (jqXHR.hasOwnProperty('responseJSON') && jqXHR.responseJSON.hasOwnProperty('error')) {
errorMessage = 'Server-Fehler: ' + jqXHR.responseJSON.error;
}
upload.elements.$statusInfo.html('' + errorMessage + '');
upload.elements.$progressBar.val(null);
},
success: function (data) {
if (data.hasOwnProperty('success') && data.success === false) {
upload.elements.$progressBar.val(null);
upload.elements.$statusInfo.html('Server-Fehler: ' + data.error + '');
return;
}
if (!data.hasOwnProperty('file') && !data.file.hasOwnProperty('bytes')) {
alert('Fehlerhafte Antwort vom Server. #1');
return;
}
if (data.file.bytes === false) {
alert('Fehlerhafte Antwort vom Server. #2');
return;
}
var bytesSend = data.file.bytes;
var sizeDone = start + bytesSend;
var sizeTotal = upload.file.size;
var percentDone = Math.floor((sizeDone / sizeTotal) * 100);
upload.elements.$progressBar.val(percentDone);
// Die erste Response vom Server enthält das PHP-Upload-Limit
// => ChunkSize anpassen falls diese über dem PHP-Limit liegt
if (data.hasOwnProperty('uploadLimit')
&& typeof data.uploadLimit === 'number'
&& data.uploadLimit > 0
) {
var uploadLimit = Math.floor(data.uploadLimit / 100 * 95); // 5% als Reserve freihalten
var transferSize = me.calculateBase64SizeFromRawSize(me.options.chunkSize);
if (uploadLimit < transferSize) {
var maxChunkSize = me.calculateRawSizeFromBase64Size(uploadLimit);
me.options.chunkSize = maxChunkSize;
console.warn('ChunkedUpload: PHP upload limit is ' + data.uploadLimit + ' bytes.');
console.warn('ChunkedUpload: Chunk size set to ' + maxChunkSize + ' bytes.');
}
}
if (offset < sizeTotal) {
me.uploadFileChunk(upload, offset);
} else {
me.finishUpload(upload.id);
}
}
});
};
// Datei einlesen starten
upload.reader.readAsDataURL(chunkBlob);
},
/**
* Erfolgreichen Upload abschließen
*
* @param {String} uploadId
*/
finishUpload: function (uploadId) {
if (me.storage.uploads.hasOwnProperty(uploadId)) {
var upload = me.storage.uploads[uploadId];
upload.elements.$statusInfo.html('Upload erfolgreich');
upload.elements.$actionCell.html('');
upload.status = STATUS.FINISHED;
// Callback aufrufen
var fileInfo = new FileInfo(upload.id, upload.file.name, upload.file.type, upload.file.size);
me.options.fileComplete(fileInfo);
}
},
/**
* Upload als fehlerhaft markieren
*
* @param {String} uploadId
* @param {String} errorMessage
*/
displayUploadError: function (uploadId, errorMessage) {
if (me.storage.uploads.hasOwnProperty(uploadId)) {
var upload = me.storage.uploads[uploadId];
upload.elements.$statusInfo.html('Upload-Fehler: ' + errorMessage);
upload.elements.$actionCell.html('');
upload.status = STATUS.FAILURE;
}
},
/**
* Datei-Tabelle erzeugen
*/
createFilesContainer: function () {
var template = '';
if(me.options.upload.view === 'sidebar') {
template +=
' | | ' +
'' +
' ';
}
else {
template +=
'Dateiname | Größe | ' +
'Fortschritt | Status | Aktionen | ' +
' | ' +
' ';
}
var $list = $(template).hide();
var $filesContainer = $(me.options.filesContainer);
if ($filesContainer.length === 0) {
$filesContainer = $('').insertAfter(me.storage.$fileInput);
}
$filesContainer.append($list);
$filesContainer.addClass('chunked-file-upload-container');
me.storage.$filesContainer = $filesContainer;
me.storage.$filesList = $list.find('tbody');
},
/**
* HTML5 File-API vorhanden? Oder Uralt-Browser?
*
* @return {boolean}
*/
isFileApiAvailable: function () {
return typeof window.File !== 'undefined' &&
typeof window.FileList !== 'undefined' &&
typeof window.FileReader !== 'undefined';
},
/**
* Zufällige ID generieren
*
* @return {string}
*/
generateRandomId: function () {
return 'chunked_upload_' + Math.floor(Math.random() * Math.floor(9999999999));
},
/**
* @param {string|number} value
*
* @return {string}
*/
formatBytes: function (value) {
var bytes = parseInt(value, 10);
if (bytes === 0) {
return '0 Bytes';
}
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
var exponent = Math.floor(Math.log(bytes) / Math.log(1024));
var decimalString = (bytes / Math.pow(1024, exponent)).toFixed(1) + '';
return decimalString.replace('.', ',') + ' ' + sizes[exponent];
},
/**
* @param {number} rawSize Größe der Binärdaten (in Bytes)
*
* @return {number} Größe in Bytes wenn Base64-kodiert
*/
calculateBase64SizeFromRawSize: function (rawSize) {
return Math.floor(rawSize / 3 * 4);
},
/**
*
* @param {number} encodedSize Größe eines Base64-kodierten Strings (in Bytes)
*
* @return {number} Größe in Bytes nach Base64-Dekodierung
*/
calculateRawSizeFromBase64Size: function (encodedSize) {
return Math.floor(encodedSize / 4 * 3);
}
};
/**
* @param {string} id Unique id; example: 'chunked_upload_2377390993'
* @param {string} name file name
* @param {string} type Mime type
* @param {number} size File size in bytes
* @constructor
*/
var FileInfo = function (id, name, type, size) {
this.id = id;
this.name = name;
this.type = type;
this.size = size;
};
me.init($elem, options);
/**
* Return public api
*/
return {};
};
// Dokumentation: Siehe Dateianfang
$.fn.chunkedUpload = function (options) {
return this.each(function () {
var $elem = $(this);
if (!$elem.data('chunkedUpload')) {
var api = new ChunkedUpload(this, options);
$elem.data('chunkedUpload', api);
}
});
};
}(jQuery));
|