大文件,也许没有你想象中那么“大”?前端大文件上传方法来了!

3,854 阅读6分钟

本文正在参加「金石计划」

前言

大文件上传?不就是传个大文件嘛,有什么难的!但是,如果你是个前端开发,你就会知道这绝对不是一件轻松的事情。上传大文件可能会遇到许多问题,例如网络带宽限制、服务器限制、数据可靠性和用户体验等等。所以,为了让用户能够顺利地上传大文件,前端需要使用一些神奇的技术和工具,如分块上传、断点续传、等等。如果你想要掌握这些技术,并实现高效、可靠、稳定的大文件上传功能,那么就跟我来吧!在这篇文章中,我将告诉你如何实现前端大文件上传,让我们一起开心地上传大文件吧!

背景

随着互联网技术的不断发展,Web应用程序越来越需要处理大文件上传的需求,例如用户上传大型视频、音频、图片等文件。然而,传统的上传方式通常受限于浏览器和服务器的限制,无法高效地处理大文件上传。因此,前端开发人员需要掌握一些技术,以便实现高效的大文件上传功能。

先来说说大文件上传的必要性:

  1. 大文件上传能够支持更大的文件传输,这对于需要传输大量数据的应用非常重要。
  2. 大文件上传能够提高数据的传输速度,节省时间和资源,特别是在需要处理大量数据的场景下。
  3. 大文件上传能够支持在线存储、备份和分享等应用,这对于云存储和数据共享非常重要。
  4. 大文件上传能够提高数据的可靠性,通过分块上传和断点续传等技术,能够避免数据丢失和上传失败等问题。
  5. 大文件上传能够支持多人协作,多人可以同时上传和编辑同一个文件,这对于团队协作非常重要。

再来聊聊大文件上传的难点:

  1. 带宽限制:上传大文件需要消耗大量的带宽,上传速度可能会很慢,甚至会中断。
  2. 服务器限制:服务器通常会限制上传文件的大小,如果上传的文件太大,可能会被服务器拒绝。
  3. 可靠性问题:上传大文件时,网络连接可能会出现问题,导致上传中断,这会导致数据丢失,因此需要在上传过程中保证数据的可靠性。
  4. 分块上传:上传大文件时,需要将文件分成多个小块,这样才能减少上传失败的可能性,并且能够提高上传速度。
  5. 断点续传:如果上传失败,需要能够从上次上传的地方继续上传,避免重新上传整个文件,这样能够节省时间和带宽。

实现方法

那下面本文将介绍如何使用JavaScriptHTML5的新特性,实现前端大文件上传的功能。将从以下三个方面来讲解:

  • 使用FormData对象实现文件上传
  • 实现分片上传,提高上传速度和稳定性
  • 使用断点续传,避免上传失败和重复上传的问题

使用FormData对象实现文件上传

HTML5中,新增了FormData对象,可以方便地将表单数据和文件一起发送到服务器。使用FormData对象实现文件上传的过程如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
    <style>
        .box {
            display: flex;
            align-items: center;
        }
        #file {
            display: block;
        }
        .button {
            display: inline-block;
            padding: 8px 20px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .button:hover {
            background-color: #3e8e41;
        }
    </style>
</head>
<body>
    <div class="box">
        <input type="file" id="file">
        <button class="button" onclick="upload()">上传</button>
    </div>

    <script>
        const url = 'http://example.com/upload'; // 替换成上传接口地址

        function upload() {
            const file = document.getElementById('file').files[0];
            const xhr = new XMLHttpRequest();
            const formData = new FormData();
            formData.append('file', file);

            fetch(url, {
                    method: 'GET', // 方法根据接口
                    params: formData, // get: params   post: body
                    headers: {
                        'Authorization': 'xxxxxx' // 替换成你自己的token
                    },
                }).then(response => {
                    console.log('上传成功', response);
                }).catch(error => {
                    console.error('上传失败', error);
                });
            }
    </script>
</body>
</html>

在上面的代码中,我们首先创建了一个FormData对象,并向其中添加了一个文件。然后,我们使用fetch方法向服务器发送GET请求,将FormData对象作为请求体。最后,我们处理服务器的响应,可以在then方法中处理成功的情况,在catch方法中处理失败的情况。

实现分片上传

对于大文件上传,我们可以将文件分成多个小块,每次上传一部分,以此提高上传速度和稳定性。下面是实现分片上传的代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
    <style>
        .box {
            display: flex;
            align-items: center;
        }
        #file {
            display: block;
        }
        .button {
            display: inline-block;
            padding: 8px 20px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .button:hover {
            background-color: #3e8e41;
        }
    </style>
</head>
<body>
    <div class="box">
        <input type="file" id="file">
        <button class="button" onclick="upload()">上传</button>
    </div>

    <script>
        const url = 'http://example.com/upload'; // 替换成上传接口地址
        const BYTES_PER_CHUNK = 1024 * 1024; // 每个分片的大小为1MB
        let start = 0;

        /**
         * 上传
         */ 
        function upload() {
            const file = document.getElementById('file').files[0];
            const totalChunks = Math.ceil(file.size / BYTES_PER_CHUNK); // 总分片数
            let uploadedChunks = 0; // 已上传分片数

            function uploadChunk(start) {
                const chunk = file.slice(start, start + BYTES_PER_CHUNK);
                const formData = new FormData();
                formData.append('file', chunk, file.name + '-' + start);

                return fetch(url, {
                    method: 'POST',
                    body: formData,
                    headers: {
                        'Authorization': 'xxxxxx' // 替换成你自己的token
                    }
                }).then(response => {
                    if (response.ok) {
                        console.log('分片上传成功');
                        uploadedChunks++;

                        // 所有分片上传完成
                        if (uploadedChunks === totalChunks) {
                            console.log('所有分片上传完成');
                            // 合并分片,完成上传
                            mergeChunks();
                        } else {
                            // 继续上传下一个分片
                            uploadChunk(start + BYTES_PER_CHUNK);
                        }
                    } else {
                        console.error('分片上传失败');
                        // 重新上传失败的分片
                        uploadChunk(start);
                    }
                }).catch(error => {
                    console.error('分片上传失败', error);
                    // 重新上传失败的分片
                    uploadChunk(start);
                });
            }

            // 上传第一个分片
            uploadChunk(start);
        }

        function mergeChunks() {
            console.log('开始合并分片');
            // 调用后端接口合并分片
            // ...
            console.log('合并分片完成');
        }
    </script>
</body>
</html>

在上面的代码中,我们首先定义了每个分片的大小为1MB,然后将文件切成多个小块,每次上传一部分。在上传时,使用了递归的方式来上传分片,当分片上传成功时,继续上传下一个分片,当分片上传失败时,重新上传失败的分片,直到分片上传成功为止。同时,在上传分片的过程中,使用了一个变量 uploadedChunks 来记录已上传的分片数量。当所有分片上传完成后,我们调用 mergeChunks 方法来合并分片,完成文件的上传。

使用断点续传

对于大文件上传,如果上传过程中出现网络中断或服务器错误,就会导致上传失败,并且需要重新上传。为了避免这种情况的发生,我们可以使用断点续传的方式,即将上传的文件分成多个小块,每次上传一部分,如果上传失败,则从上次的位置继续上传。实现断点续传的代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
    <style>
        .box {
            display: flex;
            align-items: center;
        }
        #file {
            display: block;
        }
        .button {
            display: inline-block;
            padding: 8px 20px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .button:hover {
            background-color: #3e8e41;
        }
    </style>
</head>
<body>
    <div class="box">
        <input type="file" id="file">
        <button class="button" onclick="upload()">上传</button>
    </div>

    <script>
        const url = 'http://example.com/upload'; // 替换成上传接口地址
        const BYTES_PER_CHUNK = 1024 * 1024; // 每个分片的大小为1MB
        let start = 0;
        let uploaded = 0;

        // 计算已上传的大小
        if (localStorage.getItem('uploaded')) {
            uploaded = parseInt(localStorage.getItem('uploaded'));
            start = uploaded;
        }

        function upload() {
            const file = document.getElementById('file').files[0];

            while (start < file.size) {
                const chunk = file.slice(start, start + BYTES_PER_CHUNK);
                const formData = new FormData();
                formData.append('file', chunk, file.name + '-' + start);

                fetch(url, {
                    method: 'GET', // 方法根据接口
                    params: formData, // get: params   post: body
                    headers: {
                        'Authorization': 'xxxx' // 替换成你自己的token
                    },
                }).then(response => {
                    console.log('上传成功', response);
                    uploaded += chunk.size;
                    localStorage.setItem('uploaded', uploaded);
                }).catch(error => {
                    console.error('上传失败', error);
                    // 上传失败,重置start的值,重新上传这一段分片 
                    start -= BYTES_PER_CHUNK;
                });

                start += BYTES_PER_CHUNK;
            }
        }
    </script>
</body>
</html>

在这个代码中,我们使用了BYTES_PER_CHUNK常量来定义每个分片的大小。在上传之前,我们计算出已上传的大小,并将其存储在uploaded变量和localStorage中。在上传时,我们依次将文件分片,使用fetch将每个分片通过FormData对象上传到指定的接口地址。每个分片上传成功后,我们将已上传的大小加上该分片的大小,并将结果存储到uploaded变量和localStorage中;当上传失败时,代码会将 start 的值重置回前一个分片的起始位置,即重复上传上一次上传失败的分片。这样,就可以保证每个分片都能够成功上传。这样,当用户在上传过程中断开连接或者刷新页面后,我们就可以使用localStorage中存储的已上传的大小来实现断点续传的功能。

请注意,以上代码仅为示例代码,实际使用时需根据具体情况进行修改和优化。

综上所述,我们可以使用JavaScriptHTML5的新特性,实现高效的大文件上传功能。通过使用FormData对象实现文件上传、实现分片上传和使用断点续传,可以提高上传速度和稳定性,避免上传失败和重复上传的问题。

最后,实现前端大文件上传是一项不可或缺的技能,它可以让你在面对像GIFPNGMP4这样的大文件时不再感到无从下手。当你掌握了分块上传、断点续传等技术后,你就可以在往后的工作中游刃有余。

后语

小伙伴们,如果觉得本文对你有些许帮助,点个👍或者➕个关注再走呗^_^ 。另外如果本文章有问题或有不理解的部分,欢迎大家在评论区评论指出,我们一起讨论共勉。

src=http___p6.itc.cn_q_70_images03_20210104_70f8545500034a5bae5f1695a7ce3da0.jpeg&refer=http___p6.itc.webp