获取 blob 的进度 javascript

Progress for a fetch blob javascript

我正在尝试执行 javascript 提取以使用提取获取视频文件。我能够下载文件并获取 blob URL,但我似乎无法在下载时获得进度。

我试过这个:

    let response = await fetch('test.mp4');
    
    const reader = response.body.getReader();
    
    const contentLength=response.headers.get('Content-Length');
    
    let receivedLength = 0; 
    d=document.getElementById('progress_bar');
    while(true) 
    {
        const {done, value} = await reader.read();
        
        if (done) 
        {
            break;
        }
        receivedLength += value.length;
        d.innerHTML="Bytes loaded:"+receivedLength;
    }
    
    const blob = await response.blob();
    
    var vid=URL.createObjectURL(blob);

问题是我得到“Response.blob:Body 已被消耗”。我看到 reader.read() 可能正在这样做。我怎样才能得到接收到的数据量,然后在它的末尾得到一个 blob URL?

谢谢。

更新:

我的第一次尝试是在下载块时收集它们,然后将它们放回原处,占用大量内存(视频大小的 2-3 倍)。使用 ReadableStream 的内存占用要低得多(对于 1.1GB 的 mkv,内存使用量徘徊在 150MB 左右)。代码主要改编自这里的代码片段,我只做了很少的修改:

https://github.com/AnthumChris/fetch-progress-indicators/blob/master/fetch-basic/supported-browser.js

<div id="progress_bar"></div>
<video id="video_player"></video>
    const elProgress = document.getElementById('progress_bar'),
        player = document.getElementById('video_player');

    function getVideo2() {
        let contentType = 'video/mp4';
        fetch('$pathToVideo.mp4')
            .then(response => {

                const contentEncoding = response.headers.get('content-encoding');
                const contentLength = response.headers.get(contentEncoding ? 'x-file-size' : 'content-length');
                contentType = response.headers.get('content-type') || contentType;
                if (contentLength === null) {
                    throw Error('Response size header unavailable');
                }

                const total = parseInt(contentLength, 10);
                let loaded = 0;

                return new Response(
                    new ReadableStream({
                        start(controller) {
                            const reader = response.body.getReader();

                            read();

                            function read() {
                                reader.read().then(({done, value}) => {
                                    if (done) {
                                        controller.close();
                                        return;
                                    }
                                    loaded += value.byteLength;
                                    progress({loaded, total})
                                    controller.enqueue(value);
                                    read();
                                }).catch(error => {
                                    console.error(error);
                                    controller.error(error)
                                })
                            }
                        }
                    })
                );
            })
            .then(response => response.blob())
            .then(blob => {
                let vid = URL.createObjectURL(blob);
                player.style.display = 'block';
                player.type = contentType;
                player.src = vid;
                elProgress.innerHTML += "<br /> Press play!";
            })
            .catch(error => {
                console.error(error);
            })
    }

    function progress({loaded, total}) {
        elProgress.innerHTML = Math.round(loaded / total * 100) + '%';
    }

First Attempt(较差,适合小文件)

我原来的做法。对于 1.1GB 的 mkv,在下载文件时内存使用量上升到 1.3GB,然后在组合块时飙升到大约 3.5Gb。视频开始播放后,选项卡的内存使用量会回落至约 200MB,但 Chrome 的总体使用量仍超过 1GB。

无需调用 response.blob() 来获取 blob,您可以通过累积视频的每个块 (value) 自行构建 blob。改编自这里的例子:https://javascript.info/fetch-progress#0d0g7tutne

        //...
        receivedLength += value.length;
        chunks.push(value);
        //...
        // ==> put the chunks into a Uint8Array that the Blob constructor can use
        let Uint8Chunks = new Uint8Array(receivedLength), position = 0;
        for (let chunk of chunks) {
            Uint8Chunks.set(chunk, position);
            position += chunk.length;
        }

        // ==> you may want to get the mimetype from the content-type header
        const blob = new Blob([Uint8Chunks], {type: 'video/mp4'})