// Для начала определим метод XMLHttpRequest.sendAsBinary(),
// если он не определен (Например, для браузера Google Chrome).
if (!XMLHttpRequest.prototype.sendAsBinary) {
XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
function byteValue(x) {
return x.charCodeAt(0) & 0xff;
}
var ords = Array.prototype.map.call(datastr, byteValue);
var ui8a = new Uint8Array(ords);
this.send(ui8a.buffer);
}
}
/**
* Класс FileUploader.
* @param ioptions Ассоциативный массив опций загрузки
*/
function FileUploader(ioptions) {
// Позиция, с которой будем загружать файл
this.position=0;
// Размер загружаемого файла
this.filesize=0;
// Объект Blob или File (FileList[i])
this.file = null;
// Ассоциативный массив опций
this.options=ioptions;
// Если не определена опция uploadscript, то возвращаем null. Нельзя
// продолжать, если эта опция не определена.
if (this.options['uploadscript']==undefined) return null;
/*
* Проверка, поддерживает ли браузер необходимые объекты
* @return true, если браузер поддерживает все необходимые объекты
*/
this.CheckBrowser=function() {
if (window.File && window.FileReader && window.FileList && window.Blob) return true; else return false;
}
/*
* Загрузка части файла на сервер
* @param from Позиция, с которой будем загружать файл
*/
this.UploadPortion=function(from) {
// Объект FileReader, в него будем считывать часть загружаемого файла
var reader = new FileReader();
// Текущий объект
var that=this;
// Позиция с которой будем загружать файл
var loadfrom=from;
// Объект Blob, для частичного считывания файла
var blob=null;
// Таймаут для функции setTimeout. С помощью этой функции реализована повторная попытка загрузки
// по таймауту (что не совсем корректно)
var xhrHttpTimeout=null;
/*
* Событие срабатывающее после чтения части файла в FileReader
* @param evt Событие
*/
reader.onloadend = function(evt) {
if (evt.target.readyState == FileReader.DONE) {
// Создадим объект XMLHttpRequest, установим адрес скрипта для POST
// и необходимые заголовки HTTP запроса.
var xhr = new XMLHttpRequest();
xhr.open('POST', that.options['uploadscript'], true);
xhr.setRequestHeader("Content-Type", "application/x-binary; charset=x-user-defined");
// Идентификатор загрузки (чтобы знать на стороне сервера что с чем склеивать)
xhr.setRequestHeader("Upload-Id", that.options['uploadid']);
// Позиция начала в файле
xhr.setRequestHeader("Portion-From", from);
// Размер порции
xhr.setRequestHeader("Portion-Size", that.options['portion']);
// Установим таймаут
that.xhrHttpTimeout=setTimeout(function() {
xhr.abort();
},that.options['timeout']);
/*
* Событие XMLHttpRequest.onProcess. Отрисовка ProgressBar.
* @param evt Событие
*/
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
// Посчитаем количество закаченного в процентах (с точность до 0.1)
var percentComplete = Math.round((loadfrom+evt.loaded) * 1000 / that.filesize);percentComplete/=10;
// Посчитаем ширину синей полоски ProgressBar
var width=Math.round((loadfrom+evt.loaded) * 300 / that.filesize);
// Изменим свойства элементом ProgressBar'а, добавим к нему текст
var div1=document.getElementById('cnuploader_progressbar');
var div2=document.getElementById('cnuploader_progresscomplete');
div1.style.display='block';
div2.style.display='block';
div2.style.width=width+'px';
if (percentComplete<30) {
div2.textContent='';
div1.textContent=percentComplete+'%';
}
else {
div2.textContent=percentComplete+'%';
div1.textContent='';
}
}
}, false);
/*
* Событие XMLHttpRequest.onLoad. Окончание загрузки порции.
* @param evt Событие
*/
xhr.addEventListener("load", function(evt) {
// Очистим таймаут
clearTimeout(that.xhrHttpTimeout);
// Если сервер не вернул HTTP статус 200, то выведем окно с сообщением сервера.
if (evt.target.status!=200) {
alert(evt.target.responseText);
return;
}
// Добавим к текущей позиции размер порции.
that.position+=that.options['portion'];
// Закачаем следующую порцию, если файл еще не кончился.
if (that.filesize>that.position) {
that.UploadPortion(that.position);
}
else {
// Если все порции загружены, сообщим об этом серверу. XMLHttpRequest, метод GET,
// PHP скрипт тот-же.
var gxhr = new XMLHttpRequest();
gxhr.open('GET', that.options['uploadscript']+'?action=done', true);
// Установим идентификатор загруки.
gxhr.setRequestHeader("Upload-Id", that.options['uploadid']);
/*
* Событие XMLHttpRequest.onLoad. Окончание загрузки сообщения об окончании загрузки файла :).
* @param evt Событие
*/
gxhr.addEventListener("load", function(evt) {
// Если сервер не вернул HTTP статус 200, то выведем окно с сообщением сервера.
if (evt.target.status!=200) {
alert(evt.target.responseText.toString());
return;
}
// Если все нормально, то отправим пользователя дальше. Там может быть сообщение
// об успешной загрузке или следующий шаг формы с дополнительным полями.
else window.parent.location=that.options['redirect_success'];
}, false);
// Отправим HTTP GET запрос
gxhr.sendAsBinary('');
}
}, false);
/*
* Событие XMLHttpRequest.onError. Ошибка при загрузке
* @param evt Событие
*/
xhr.addEventListener("error", function(evt) {
// Очистим таймаут
clearTimeout(that.xhrHttpTimeout);
// Сообщим серверу об ошибке во время загруке, сервер сможет удалить уже загруженные части.
// XMLHttpRequest, метод GET, PHP скрипт тот-же.
var gxhr = new XMLHttpRequest();
gxhr.open('GET', that.options['uploadscript']+'?action=abort', true);
// Установим идентификатор загруки.
gxhr.setRequestHeader("Upload-Id", that.options['uploadid']);
/*
* Событие XMLHttpRequest.onLoad. Окончание загрузки сообщения об ошибке загрузки :).
* @param evt Событие
*/
gxhr.addEventListener("load", function(evt) {
// Если сервер не вернул HTTP статус 200, то выведем окно с сообщением сервера.
if (evt.target.status!=200) {
alert(evt.target.responseText);
return;
}
}, false);
// Отправим HTTP GET запрос
gxhr.sendAsBinary('');
// Отобразим сообщение об ошибке
if (that.options['message_error']==undefined) alert("There was an error attempting to upload the file."); else alert(that.options['message_error']);
}, false);
/*
* Событие XMLHttpRequest.onAbort. Если по какой-то причине передача прервана, повторим попытку.
* @param evt Событие
*/
xhr.addEventListener("abort", function(evt) {
clearTimeout(that.xhrHttpTimeout);
that.UploadPortion(that.position);
}, false);
// Отправим порцию методом POST
xhr.sendAsBinary(evt.target.result);
}
};
that.blob=null;
// Считаем порцию в объект Blob. Три условия для трех возможных определений Blob.[.*]slice().
if (this.file.slice) that.blob=this.file.slice(from,from+that.options['portion']);
else {
if (this.file.webkitSlice) that.blob=this.file.webkitSlice(from,from+that.options['portion']);
else {
if (this.file.mozSlice) that.blob=this.file.mozSlice(from,from+that.options['portion']);
}
}
// Считаем Blob (часть файла) в FileReader
reader.readAsBinaryString(that.blob);
}
/*
* Загрузка файла на сервер
* return Число. Если не 0, то произошла ошибка
*/
this.Upload=function() {
// Скроем форму, чтобы пользователь не отправил файл дважды
var e=document.getElementById(this.options['form']);
if (e) e.style.display='none';
if (!this.file) return -1;
else {
// Если размер файла больше размера порциии ограничимся одной порцией
if (this.filesize>this.options['portion']) this.UploadPortion(0,this.options['portion']);
// Иначе отправим файл целиком
else this.UploadPortion(0,this.filesize);
}
}
if (this.CheckBrowser()) {
// Установим значения по умолчанию
if (this.options['portion']==undefined) this.options['portion']=1048576;
if (this.options['timeout']==undefined) this.options['timeout']=15000;
var that = this;
// Добавим обработку события выбора файла
document.getElementById(this.options['formfiles']).addEventListener('change', function (evt) {
var files=evt.target.files;
// Выберем только первый файл
for (var i = 0, f; f = files[i]; i++) {
that.filesize=f.size;
that.file = f;
break;
}
}, false);
// Добавим обработку события onSubmit формы
document.getElementById(this.options['form']).addEventListener('submit', function (evt) {
that.Upload();
(arguments[0].preventDefault)? arguments[0].preventDefault(): arguments[0].returnValue = false;
}, false);
}
}