• javascript 大文件下载,分片下载,断点续传


    javascript 大文件下载,分片下载,断点续传

    既然是断点续传,自然离不开分片下载。接下来,我们将一个文件分片并一个一个下载。

    首先,我们需要获取文件的大小,以便更好地对其进行分段。

    1:获取文件大小:

    // status
    const DONE = 4;
    
    // get content length
    function getContentLength(url) {
        return new Promise((resolve, reject) => {
            var xhr = new XMLHttpRequest();
            xhr.open('HEAD', url, true);
            xhr.onreadystatechange = function () {
                if (this.readyState == DONE) {
                    resolve(this.getResponseHeader('Content-Length'));
                }
            }
            xhr.send();
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    得到文件大小后,对其进行切片,得到对应切片的内容。

    因为这里的测试文件不是很大,所以每个切片的大小设置为100。

    具体大小视个人需求而定。

    2:切片下载

    // status
    const DONE = 4;
    
    // range size
    const RANGE_SIZE = 100;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    // get range content
    function getRangeContent(startIndex, endIndex, url) {
        return new Promise((resolve, reject) => {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', url, true);
            xhr.setRequestHeader('Range', `bytes=${startIndex}-${endIndex}`);
            xhr.responseType = 'arraybuffer';
            xhr.onreadystatechange = function () {
                if (this.readyState == DONE) {
                    resolve(this.response);
                }
            }
            xhr.send();
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    因为是arraybuffer类型的内容,这里我们使用Uint8Array视图来接受合并。

    3:合并数据

    // concat arraybuffer array
    function concatArrayBuffer(arrayBufferArray) {
        let totalLength = 0;
        arrayBufferArray.forEach(arrayBuffer => {
            totalLength += arrayBuffer.byteLength;
        });
        const result = new Uint8Array(totalLength);
        let offset = 0;
        arrayBufferArray.forEach(arrayBuffer => {
            result.set(new Uint8Array(arrayBuffer), offset);
            offset += arrayBuffer.byteLength;
        });
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    如果您有下载的需要,这里有一个示例,您可以将合并后的数据下载到本地。

    4:下载到本地

    // download arraybuffer file
    function downloadArrayBufferFile(arrayBuffer, fileName) {
        const blob = new Blob([arrayBuffer], { type: 'application/octet-stream' });
        const a = document.createElement('a');
        a.href = URL.createObjectURL(blob);
        a.download = fileName;
        a.click();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后你就可以得到完整的内容了。

    5:成功

    // main methoeds
    async function main() {
        const fileUrl = 'http://localhost:8083/public/m2.jpeg';
        const contentLength = await getContentLength(fileUrl);
        const numberRequest = Math.ceil(contentLength / RANGE_SIZE);
        const arrayBufferArray = [];
        for (let i = 0; i < numberRequest; i++) {
            const startIndex = i * RANGE_SIZE;
            const endIndex = startIndex + RANGE_SIZE - 1;
            const clip = await getRangeContent(startIndex, endIndex, fileUrl);
            arrayBufferArray.push(clip);
        }
        const result = concatArrayBuffer(arrayBufferArray);
        downloadArrayBufferFile(result, 'access.txt');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    6:完整代码

    // status
    const DONE = 4;
    
    // range size
    const RANGE_SIZE = 100;
    
    // get content length
    function getContentLength(url) {
        return new Promise((resolve, reject) => {
            var xhr = new XMLHttpRequest();
            xhr.open('HEAD', url, true);
            xhr.onreadystatechange = function () {
                if (this.readyState == DONE) {
                    resolve(this.getResponseHeader('Content-Length'));
                }
            }
            xhr.send();
        })
    }
    
    // get range content
    function getRangeContent(startIndex, endIndex, url) {
        return new Promise((resolve, reject) => {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', url, true);
            xhr.setRequestHeader('Range', `bytes=${startIndex}-${endIndex}`);
            xhr.responseType = 'arraybuffer';
            xhr.onreadystatechange = function () {
                if (this.readyState == DONE) {
                    console.log(this.response)
                    resolve(this.response);
                }
            }
            xhr.send();
        })
    }
    
    // download arraybuffer file
    function downloadArrayBufferFile(arrayBuffer, fileName) {
        const blob = new Blob([arrayBuffer], { type: 'application/octet-stream' });
        const a = document.createElement('a');
        a.href = URL.createObjectURL(blob);
        a.download = fileName;
        a.click();
    }
    
    // concat arraybuffer array
    function concatArrayBuffer(arrayBufferArray) {
        let totalLength = 0;
        arrayBufferArray.forEach(arrayBuffer => {
            totalLength += arrayBuffer.byteLength;
        });
        const result = new Uint8Array(totalLength);
        let offset = 0;
        arrayBufferArray.forEach(arrayBuffer => {
            result.set(new Uint8Array(arrayBuffer), offset);
            offset += arrayBuffer.byteLength;
        });
        return result;
    }
    
    // main methoeds
    async function main() {
        const fileUrl = 'http://localhost:8083/public/access.txt';
        const contentLength = await getContentLength(fileUrl);
        const numberRequest = Math.ceil(contentLength / RANGE_SIZE);
        const arrayBufferArray = [];
        for (let i = 0; i < numberRequest; i++) {
            const startIndex = i * RANGE_SIZE;
            const endIndex = startIndex + RANGE_SIZE - 1;
            const clip = await getRangeContent(startIndex, endIndex, fileUrl);
            arrayBufferArray.push(clip);
        }
        const result = concatArrayBuffer(arrayBufferArray);
        downloadArrayBufferFile(result, 'access.txt');
    }
    
    main();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    至此,你应该有断点续传的想法了。
    只需要对已经分片的数据进行标记,最后将标记好的分片数据进行合并,就可以得到一个完整的文件。
    即使途中网络中断,只要正确标记了段数据,没有获取到哪个段数据,就可以从服务器重新获取丢失的段数据到本地,合并完整的内容并再次下载。
    如果还是不知道怎么做,可以给我留言,接下来我会介绍前后端配合标记验证数据。

  • 相关阅读:
    java 多线程 Thread 互斥锁
    【机器学习4】降维
    用Python写一个自动下载视频、弹幕、评论的软件(2022最新)
    2022年 hust OJ 最新搭建方式
    SCS【5】单细胞转录组数据可视化分析 (scater)
    【BurpSuite】插件学习之dotnet-Beautifier
    人工智能|AI作画体验:见识AI绘画,离谱的创造力
    pgzrun 拼图游戏制作过程详解(1,2)
    计算机网络——物理层(数字传输系统)
    SpringBoot面试题7:SpringBoot支持什么前端模板?
  • 原文地址:https://blog.csdn.net/qq_41974199/article/details/127680629