2016-02-29 3 views
37

Я изо всех сил пытаюсь найти документацию или примеры реализации индикатора прогресса загрузки, используя fetch.Загрузить индикаторы выполнения для извлечения?

This is the only reference I've found so far, в котором говорится:

события Прогресс являются высокая черта уровня, который не поступит в выборку сейчас. Вы можете создать свой собственный, просмотрев заголовок Content-Length и используя сквозной поток для отслеживания полученных байтов.

Это означает, что вы можете явно обрабатывать ответы без использования Content-Length по-разному. И, конечно, даже если Content-Length есть, это может быть ложью. С потоками вы можете справиться с этой ложью, как хотите.

Как я могу написать «передаваемый поток для отслеживания байтов»? Если это имеет какое-то значение, я пытаюсь сделать это, чтобы загрузить изображения из браузера в Cloudinary.

ПРИМЕЧАНИЕ: Я не заинтересованы в Cloudinary JS library, так как это зависит от JQuery и мое приложение не делает. Меня интересует только обработка потока, необходимая для этого, с помощью встроенного javascript и Gifub's fetch polyfill.


https://fetch.spec.whatwg.org/#fetch-api

+3

@Magix см [Aborting выборки: The Next Generation # 447]. (Https: // GitHub.com/whatwg/fetch/issues/447) – guest271314

ответ

23

Потоки начинают приземляться в веб-платформе (https://jakearchibald.com/2016/streams-ftw/), но все еще ранние дни.

Вскоре вы сможете предоставить поток в качестве тела запроса, но открытым вопросом является вопрос о том, относится ли потребление этого потока к загруженным байтам.

Особые перенаправления могут привести к повторной передаче данных в новое местоположение, но потоки не могут «перезапускаться». Мы можем исправить это, превратив тело в обратный вызов, который можно вызвать несколько раз, но мы должны быть уверены, что разоблачение количества переадресаций не является утечкой безопасности, так как это будет первый раз на платформе JS, обнаружить это.

Некоторые из них задаются вопросом, имеет ли смысл связывать потребление потока с загруженными байтами.

Короче говоря: это еще не возможно, но в будущем это будет обрабатываться потоками или каким-либо обратным вызовом более высокого уровня, переданным в fetch().

+4

Слишком плохо. Принимая это сейчас, но когда это станет реальностью, я надеюсь, что кто-то еще опубликует обновленное решение! :) – neezer

+0

@ jaffa-the-cake любые новости? – mu3

+1

Обновление - отображение прогресса с использованием API-интерфейса с помощью потоков - https://twitter.com/umaar/status/917789464658890753/photo/1 –

5

Я не думаю, что это возможно. В проекте говорится:

это в настоящее время отсутствует [по сравнению с XHR], когда речь идет просить прогрессию


(старый ответ):
Первый пример Fetch API chapter дает представление о том, как:

Если вы хотите получить данные тела постепенно:

function consume(reader) { 
    var total = 0 
    return new Promise((resolve, reject) => { 
    function pump() { 
     reader.read().then(({done, value}) => { 
     if (done) { 
      resolve() 
      return 
     } 
     total += value.byteLength 
     log(`received ${value.byteLength} bytes (${total} bytes in total)`) 
     pump() 
     }).catch(reject) 
    } 
    pump() 
    }) 
} 

fetch("/music/pk/altes-kamuffel.flac") 
    .then(res => consume(res.body.getReader())) 
    .then(() => log("consumed the entire body without keeping the whole thing in memory!")) 
    .catch(e => log("something went wrong: " + e)) 

Помимо их использования Promise constructor antipattern, вы можете увидеть, что response.body это поток, из которого вы можете прочитать побайтно с помощью чтения, и вы может запускать событие или делать то, что вам нравится (например, запишите ход выполнения) для каждого из них.

Однако Streams spec не подходит, и я понятия не имею, работает ли это уже в любой реализации выборки.

+5

Если я правильно прочитал этот пример, это будет для ** загрузки ** файла через 'fetch'. Я заинтересован в индикаторах прогресса для ** загрузки ** файла. – neezer

+0

К сожалению, эта цитата говорит о * получении * байтов, что меня смутило. – Bergi

+0

@ Bergi Note, конструктор 'Promise' не нужен. 'Response.body.getReader()' возвращает 'Promise'. См. [Как решить Uncaught RangeError при загрузке большого размера json] (http://stackoverflow.com/questions/39959467/how-to-solve-uncaught-rangeerror-when-download-large-size-json) – guest271314

2

Возможный обходной путь будет использовать new Request() конструктор затем проверить Request.bodyUsedBoolean атрибут

геттер атрибута bodyUsed должен вернуть истину, если disturbed и ложное в противном случае.

, чтобы определить, является ли поток distributed

Объект реализации Body Mixin называется disturbed, если body не равен нулю, и его stream является disturbed.

Верните fetch()Promise изнутри .then() прикован к рекурсивным .read() зову ReadableStream когда Request.bodyUsed равно true.

Обратите внимание, что подход не считывает байты Request.body, когда байты передаются в конечную точку. Кроме того, загрузка может завершиться задолго до того, как любой ответ будет полностью возвращен браузеру.

const [input, progress, label] = [ 
    document.querySelector("input") 
    , document.querySelector("progress") 
    , document.querySelector("label") 
]; 

const url = "/path/to/server/"; 

input.onmousedown =() => { 
    label.innerHTML = ""; 
    progress.value = "0" 
}; 

input.onchange = (event) => { 

    const file = event.target.files[0]; 
    const filename = file.name; 
    progress.max = file.size; 

    const request = new Request(url, { 
    method: "POST", 
    body: file, 
    cache: "no-store" 
    }); 

    const upload = settings => fetch(settings); 

    const uploadProgress = new ReadableStream({ 
    start(controller) { 
     console.log("starting upload, request.bodyUsed:", request.bodyUsed); 
     controller.enqueue(request.bodyUsed); 
    }, 
    pull(controller) { 
     if (request.bodyUsed) { 
     controller.close(); 
     } 
     controller.enqueue(request.bodyUsed); 
     console.log("pull, request.bodyUsed:", request.bodyUsed); 
    }, 
    cancel(reason) { 
     console.log(reason); 
    } 
    }); 

    const [fileUpload, reader] = [ 
    upload(request) 
    .catch(e => { 
     reader.cancel(); 
     throw e 
    }) 
    , uploadProgress.getReader() 
    ]; 

    const processUploadRequest = ({value, done}) => { 
    if (value || done) { 
     console.log("upload complete, request.bodyUsed:", request.bodyUsed); 
     // set `progress.value` to `progress.max` here 
     // if not awaiting server response 
     // progress.value = progress.max; 
     return reader.closed.then(() => fileUpload); 
    } 
    console.log("upload progress:", value); 
    progress.value = +progress.value + 1; 
    return reader.read().then(result => processUploadRequest(result)); 
    }; 

    reader.read().then(({value, done}) => processUploadRequest({value,done})) 
    .then(response => response.text()) 
    .then(text => { 
    console.log("response:", text); 
    progress.value = progress.max; 
    input.value = ""; 
    }) 
    .catch(err => console.log("upload error:", err)); 

} 
3

Поскольку ни один из ответов не решить эту проблему.

Только для реализации вы можете определить скорость выгрузки with some small initial chunk of known size, а время загрузки можно рассчитать с использованием длины контента/скорости загрузки. Вы можете использовать это время как оценку.

+1

Очень умный, приятный трюк для использования, пока мы ждем решения в реальном времени :) – Magix

0

Основная деталь ReadableStreamobj_response .body ≫.

Пример:

let parse=_/*result*/=>{ 
    console.log(_) 
    //... 
    return /*cont?*/_.value?true:false 
} 

fetch(''). 
then(_=>(a/*!*/=_.body.getReader(), b/*!*/=z=>a.read().then(parse).then(_=>(_?b:z=>z)()), b())) 

Вы можете проверить запустив его на огромной странице, например https://html.spec.whatwg.org/ и https://html.spec.whatwg.org/print.pdf. CtrlShiftJ и загрузить код в

(Проверено на Chrome.)

0
const req = await fetch('./foo.json'); 
const total = Number(req.headers.get('content-length')); 
let loaded = 0; 
for await(const {length} of req.body.getReader()) { 
    loaded = += length; 
    const progress = ((loaded/total) * 100).toFixed(2); // toFixed(2) means two digits after floating point 
    console.log(`${progress}%`); // or yourDiv.textContent = `${progress}%`; 
} 
+0

это никогда не будет работать, так как 'ReadableStreamDefaultReader' не имеет свойства' byteLength'. – silicakes

+0

Я хочу отдать должное Бенджамину Грюнбауму за весь ответ. Потому что я узнал об этом из своей лекции. –

Смежные вопросы