Страницы

Поиск по вопросам

среда, 13 февраля 2019 г.

XMLHttpRequest: Как возобновить закачку

Тут я пытался что то навалять примерно так:
var slice = file.slice(10, 100); // прочитать байты с 10 го по 99 й
xhr.send(slice); //и отправить эти байты в запросе.
Но такая модель не жизнеспособна!
Как отослать на сервер не весь файл, а только нужную часть его?
Или поставить на паузу закачку а потом либо отменить либо продолжить.
С помощью javascript как можно это сделать?
Спасибо.


Ответ

Для загрузки нам нужно точно знать количество загруженных байт. Это может сообщить только сервер.
Алгоритм возобновляемой загрузки
Загрузкой файла будет заведовать объект Uploader, его примерный общий вид:
function Uploader(file, onSuccess, onFail, onProgress) {
// fileId уникальным образом идентифицирует файл // можно добавить идентификатор сессии посетителя, но он и так будет в заголовках var fileId = file.name + '-' + file.size + '-' + +file.lastModifiedDate;
// сделать из fileId число (хеш, алгоритм неважен), мы будем передавать его в заголовке, // в заголовках разрешены только ASCII-символы fileId = hashCode(fileId);
var errorCount = 0;
// если количество ошибок подряд превысит MAX_ERROR_COUNT, то стоп var MAX_ERROR_COUNT = 6;
var startByte = 0;
var xhrUpload; var xhrStatus;
function upload() { console.log("upload: check status"); xhrStatus = new XMLHttpRequest();
xhrStatus.onload = xhrStatus.onerror = function() {
if (this.status == 200) { startByte = +this.responseText || 0; console.log("upload: startByte=" + startByte); send(); return; }
// что-то не так if (errorCount++ < MAX_ERROR_COUNT) { setTimeout(upload, 1000 * errorCount); // через 1 сек пробуем ещё раз } else { onError(this.statusText); }
};
xhrStatus.open("GET", "status", true); xhrStatus.setRequestHeader('X-File-Id', fileId); xhrStatus.send(); }
function send() {
xhrUpload = new XMLHttpRequest(); xhrUpload.onload = xhrUpload.onerror = function() { console.log("upload end status:" + this.status + " text:" + this.statusText);
if (this.status == 200) { // успешное завершение загрузки onSuccess(); return; }
// что-то не так if (errorCount++ < MAX_ERROR_COUNT) { setTimeout(resume, 1000 * errorCount); // через 1,2,4,8,16 сек пробуем ещё раз } else { onError(this.statusText); } };
xhrUpload.open("POST", "upload", true); // какой файл догружаем /загружаем xhrUpload.setRequestHeader('X-File-Id', fileId);
xhrUpload.upload.onprogress = function(e) { errorCount = 0; onProgress(startByte + e.loaded, startByte + e.total); }
// отослать, начиная с байта startByte xhrUpload.send(file.slice(startByte)); }
function pause() { xhrStatus && xhrStatus.abort(); xhrUpload && xhrUpload.abort(); }
this.upload = upload; this.pause = pause; }
// вспомогательная функция: получение 32-битного числа из строки
function hashCode(str) { if (str.length == 0) return 0;
var hash = 0, i, chr, len; for (i = 0; i < str.length; i++) { chr = str.charCodeAt(i); hash = ((hash << 5) - hash) + chr; hash |= 0; // Convert to 32bit integer } return hash; };
Аргументы для new Uploader:
file
Объект File API. Может быть получен из формы, либо как результат Drag’n’Drop. onSuccess, onFail, onProgress Функции-коллбэки, которые будут вызываться в процессе (onProgress) и при окончании загрузки. Подробнее про важные данные, с которыми мы будем работать в процессе загрузки:
fileId
Уникальный идентификатор файла, генерируется по имени, размеру и дате модификации. По нему мы всегда сможем возобновить загрузку, в том числе и после закрытия и открытия браузера.
startByte
С какого байта загружать. Изначально – с нулевого.
errorCount / MAX_ERROR_COUNT
Текущее число ошибок / максимальное число ошибок подряд, после которого загрузка считается проваленной. Алгоритм загрузки:
Генерируем fileId из названия, размера, даты модификации файла. Можно добавить и идентификатор посетителя. Спрашиваем сервер, есть ли уже такой файл, и если да – сколько байт уже загружено? Отсылаем файл с позиции, которую сказал сервер. При этом загрузку можно прервать в любой момент, просто оборвав все запросы.
Вы можете скачать пример и запустить локально для полноценной демонстрации:

Комментариев нет:

Отправить комментарий