文件下载请求中无法获取文件下载的进度

一、概述

项目中,有时需要在客户端进行大文件的下载,如果在下载的过程中只是一味的让用户进行等待,体验很不好。

所以,一般都会在下载的过程中显示一个下载的进度条,以便让用户知道下载的进度,还有多久能下载完成,不至于让用户在加载的过程中失去信心。

进度条的计算公式也很简单,如下所示。

进度条计算公式

// progress:进度百分比
// downloadedSize:已经下载成功的大小
// totalSize:文件总大小
let progress = parseInt(downloadedSize / totalSize * 100);

原本是一个不算难的需求,但是在获取进度的过程中,却出现了问题,获取的totalSize一直为0,只有等文件下载完成之后,才能获取totalSize

二、解决思路

文件请求使用的是5+ API中的Downloader模块中的方法。代码如下。

// 创建下载任务
const downloadTask = plus.downloader.createDownload('文件地址', {}, (download, status) => {
    if(status === 200){
        console.log(`file download success:${download.filename}`);
    }else {
        console.error(`file download fail, status code: ${status}`);
    }
})
// 监听下载状态改变事件
downloadTask.addEventListener('statechanged'. (download, status) => {
    // 状态为3表示正在下载任务接收数据(下载过程中会多次调用)
    if (download.state == 3) {
        console.log("download.downloadedSize: ", download.downloadedSize);
        console.log("download.totalSize: ", download.totalSize);
        // 进度计算
        let progress = parseInt(download.downloadedSize / download.totalSize * 100);
    }
})

上述程序代码中,理论上是可以正常获取进度的,然后实际执行结果却不如人意。

不知为何,downloadedSize可以正常获取到值。但是,totalSize的值却一直为0。

在查阅了官方文档之后,发现其中有这么一句话,具体可以看链接

这里说:totalSize的值时从HTTP请求响应头数据里面的Content-Length中获取,如果没有这个值,则totalSize的值便会一直为0。

但笔者通过抓包的形式,查看了一下这个文件的请求响应数据。发现在响应的头数据是包含了Content-Length数据的,明显和官网说的不符。

在经过一番查阅之后,笔者终于发现这个问题其实跟HTTP协议中的压缩编码传输有关。其中涉及到了两个请求头,accept-encodingcontent-encoding
  
accept-encoding是request headers中的一个数据,是用来发送请求时告知服务器,本地的客户端支持什么格式的压缩编码。

content-encodingresponse headers中的一个数据,用来在数据响应时由服务器告知客户端,本次的响应数据中,使用了什么格式的压缩编码,如果未使用任何编码,则为空,如果指定了使用编码,则本地客户端要根据编码格式对数据进行解压缩。

问题的关键就出在了这里,如果请求中的数据使用了压缩编码,就无法获取精确的进度运算。

再次通过抓包,发现文件请求中accept-encoding确实包含了gzip,服务器响应中的content-encoding的值也包含了gzip。证实请求的数据确实进行了gzip编码压缩。

原本笔者认为这是5+ API中的缺陷,然而经过笔者的求证之后,发现使用js原生的XMLHttpRequest对象,也会出现这种问题。只有Firefox浏览器是例外,应该是Firefox本身对此问题做了兼容。

三、解决方案

在通过上述过程知道其中的原理之后,我们便有了解决的方向。

既然是请求的数据经过了压缩编码之后导致无法获取进度信息,那我们就设置需要获取进度的请求不要进行编码压缩。

修改后的代码

// 创建下载任务
const downloadTask = plus.downloader.createDownload('文件地址', {}, (download, status) => {
    if(status === 200){
        console.log(`file download success:${download.filename}`);
    }else {
        console.error(`file download fail, status code: ${status}`);
    }
})
// 设置请求的请求头accept-encoding信息为空,即告诉服务器不要进行编码压缩
// 此处的空字符串一直要加一个空格,不然在IOS能有效覆盖重写请求头信息,在Android中,会无效。
// downloadTask.setRequestHeader('accept-encoding', '') // 错误写法
downloadTask.setRequestHeader('accept-encoding', ' ') // 正确写法
// 监听下载状态改变事件
downloadTask.addEventListener('statechanged'. (download, status) => {
    // 状态为3表示正在下载任务接收数据(下载过程中会多次调用)
    if (download.state == 3) {
        console.log("download.downloadedSize: ", download.downloadedSize);
        console.log("download.totalSize: ", download.totalSize);
        // 进度计算
        let progress = parseInt(download.downloadedSize / download.totalSize * 100);
    }
})

上述解决方式,其实也是有缺陷的,gzip编码压缩的压缩比其实挺大的。大概有25%左右。一个8M的文件,可以压缩到6M上下。

过也有另外一种解决方式,就是在响应中自定义请求头,返回文件的总大小,然后通过getResponseHeader方法获取文件的总大小。因为在上述打印结果中,只有totalSize是获取不到的,downloadedSize是可以正常获取的。

从理论上讲,笔者认为是可行的。不过笔者觉得有点麻烦,就没有进行实际验证。感兴趣的读者可以尝试一下。

要在前端下载文件并获取下载进度,可以使用 XMLHttpRequest 对象来实现。以下是一个简单的示例代码: ```javascript function downloadFile(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'blob'; xhr.onload = function() { if (xhr.status === 200) { resolve(xhr.response); } else { reject(new Error('File download failed')); } }; xhr.onprogress = function(event) { if (event.lengthComputable) { const progress = Math.round((event.loaded / event.total) * 100); console.log(`Download progress: ${progress}%`); } }; xhr.onerror = function() { reject(new Error('File download failed')); }; xhr.send(); }); } // 示例用法 const fileUrl = 'https://example.com/file.pdf'; downloadFile(fileUrl) .then(blob => { // 下载完成后的处理 const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'file.pdf'; a.click(); }) .catch(error => { console.error(error); }); ``` 上述代码通过 XMLHttpRequest 发起了一个 GET 请求来下载文件,设置了 `responseType` 为 `blob`,这样可以获取到文件的二进制数据。通过监听 `onprogress` 事件,可以获取到下载进度的信息。在下载完成后,将下载的文件转换为 Blob 对象,并创建一个隐藏的 `<a>` 元素来实现文件的下载。 注意:由于涉及到跨域请求,请确保服务器端已经设置了正确的 CORS 头部,允许前端访问下载文件的资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值