导读:本期聚焦于小伙伴创作的《使用Fetch API实现文件上传进度跟踪:原生流封装与XMLHttpRequest替代方案详解》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《使用Fetch API实现文件上传进度跟踪:原生流封装与XMLHttpRequest替代方案详解》有用,将其分享出去将是对创作者最好的鼓励。

使用Fetch接口跟踪文件上传进度

在前端开发场景中,文件上传是常见需求,很多场景下还需要实时展示上传进度,提升用户体验。传统的文件上传通常会使用XMLHttpRequest实现进度监听,不过随着Fetch API的普及,很多开发者也希望借助Fetch完成相关功能。但实际上,原生的Fetch API本身并不支持进度跟踪,我们需要结合ReadableStream和手动处理请求体来实现上传进度的监控。

为什么原生Fetch无法直接跟踪上传进度

原生Fetch API的设计更加简洁,专注于请求和响应的核心逻辑,并没有提供类似XMLHttpRequestonprogress事件回调。它的请求体在被发送时,不会对外暴露已上传的字节数信息,因此无法直接获取上传进度。如果要使用Fetch实现进度跟踪,我们需要对上传的数据流进行手动封装,实时计算已发送的数据量。

实现思路

核心思路是将待上传的文件数据转换为可追踪的流,在流的处理过程中记录已发送的字节数,再通过回调或者状态更新将进度信息同步到页面上。具体步骤如下:

  • 获取需要上传的文件对象,计算文件总大小

  • 将文件数据封装为可追踪的ReadableStream,在流的分块发送过程中累计已发送的字节数

  • 使用Fetch发送封装后的请求体,同时通过定时器或者回调实时更新进度状态

完整实现示例

以下是一个基于原生Fetch和ReadableStream实现文件上传进度跟踪的完整示例,假设上传接口地址为https://www.ipipp.com/api/upload:

// 上传进度回调函数类型定义
/**
 * @param {number} progress 上传进度,范围0-100
 * @param {number} loaded 已上传字节数
 * @param {number} total 总字节数
 */
function uploadFileWithProgress(file, onProgress) {
    const totalSize = file.size;
    let loadedSize = 0;

    // 创建可读流,分块读取文件并累计已上传大小
    const readableStream = new ReadableStream({
        start(controller) {
            const reader = file.stream().getReader();
            // 递归读取文件流
            function read() {
                reader.read().then(({ done, value }) => {
                    if (done) {
                        controller.close();
                        return;
                    }
                    // 累计已上传字节数
                    loadedSize += value.length;
                    // 计算进度并回调
                    const progress = Math.round((loadedSize / totalSize) * 100);
                    onProgress(progress, loadedSize, totalSize);
                    // 将分块数据放入流中
                    controller.enqueue(value);
                    read();
                });
            }
            read();
        }
    });

    // 发起Fetch请求
    return fetch('https://www.ipipp.com/api/upload', {
        method: 'POST',
        body: readableStream,
        headers: {
            'Content-Type': file.type || 'application/octet-stream'
        }
    }).then(response => {
        if (!response.ok) {
            throw new Error('上传失败,状态码:' + response.status);
        }
        return response.json();
    });
}

// 使用示例
const fileInput = document.querySelector('#fileInput');
const progressBar = document.querySelector('#progressBar');
const progressText = document.querySelector('#progressText');

fileInput.addEventListener('change', async (e) => {
    const file = e.target.files[0];
    if (!file) return;

    try {
        const result = await uploadFileWithProgress(file, (progress, loaded, total) => {
            // 更新进度展示
            progressBar.value = progress;
            progressText.textContent = `上传进度:${progress}%,已上传${loaded}字节,总大小${total}字节`;
        });
        console.log('上传成功,返回结果:', result);
        alert('文件上传成功');
    } catch (error) {
        console.error('上传出错:', error);
        alert('文件上传失败:' + error.message);
    }
});

对应的HTML结构示例:

<input type="file" id="fileInput" />
<progress id="progressBar" value="0" max="100"></progress>
<p id="progressText">上传进度:0%</p>

注意事项

  • 该实现依赖ReadableStreamfile.stream()API,需要确保运行环境支持这些特性,现代浏览器基本都已经支持,如果需要兼容旧环境可能需要额外做polyfill处理。

  • 由于Fetch本身不会拦截请求体的发送过程,进度的计算是基于流的分块处理,和实际网络发送可能存在极小的偏差,对于大多数业务场景来说可以忽略。

  • 如果服务端接口有额外的参数要求,比如需要携带token、其他表单字段,可以在Fetch的headers中添加对应参数,或者将额外参数和文件流合并处理后再发送。

  • 上传大文件时建议结合服务端的分片上传接口使用,避免单个请求体积过大导致超时或者失败,此时进度计算可以基于分片的完成情况累加实现。

替代方案

如果不需要强制使用Fetch接口,XMLHttpRequest是更简单的实现进度跟踪的选择,它原生支持onprogress事件,代码更加简洁。以下是XMLHttpRequest实现上传进度的简单示例:

function uploadFileWithXHR(file, onProgress) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', 'https://www.ipipp.com/api/upload');

        // 监听上传进度
        xhr.upload.onprogress = (e) => {
            if (e.lengthComputable) {
                const progress = Math.round((e.loaded / e.total) * 100);
                onProgress(progress, e.loaded, e.total);
            }
        };

        xhr.onload = () => {
            if (xhr.status >= 200 && xhr.status < 300) {
                resolve(JSON.parse(xhr.responseText));
            } else {
                reject(new Error('上传失败,状态码:' + xhr.status));
            }
        };

        xhr.onerror = () => reject(new Error('网络错误,上传失败'));

        const formData = new FormData();
        formData.append('file', file);
        xhr.send(formData);
    });
}

可以根据实际项目需求选择合适的实现方式,如果项目已经统一使用Fetch进行请求处理,那么可以通过上述的流封装方式实现进度跟踪;如果更看重实现简单性,XMLHttpRequest是更优的选择。

FetchAPI 文件上传 进度跟踪 ReadableStream XMLHttpRequest

免责声明:已尽一切努力确保本网站所含信息的准确性。网站部分内容来源于网络或由用户自行发表,内容观点不代表本站立场。本站是个人网站免费分享,内容仅供个人学习、研究或参考使用,如内容中引用了第三方作品,其版权归原作者所有。若内容触犯了您的权益,请联系我们进行处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。前端、网络、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握网站开发与运维所需的核心技术栈。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端逻辑,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。