本文未完结,持续更新中......

从指定URL下载网络资源

/**
 *
 * @param url 欲下载资源的URL
 * @param {function} loading_callback - 下载进度回调函数,参数为下载进度百分比(精确到两位小数)
 * @param {function} finished_callback - 下载完成回调函数,参数为下载任务ID
 * @param {number} id - 下载任务ID
 * @returns {Promise<Blob>} - 包含下载内容的 Blob 对象的 Promise
 */
export function downloadWithProgress(url, loading_callback, finished_callback, id) {
    return new Promise((resolve, reject) => {
        fetch(url).then(response => {
            const totalBytes = response.headers.get('Content-Length');
            let downloadedBytes = 0;

            const reader = response.body.getReader();
            const chunks = [];

            function read() {
                reader.read().then(({ done, value }) => {
                    if (done) {
                        const blob = new Blob(chunks);
                        finished_callback && finished_callback(id);
                        resolve(blob);
                        return;
                    }

                    chunks.push(value);
                    downloadedBytes += value.length;

                    const progress = (downloadedBytes / totalBytes) * 100;
                    loading_callback && loading_callback(progress.toFixed(2), id);
                    // console.log(`下载进度:${progress.toFixed(2)}%`);

                    // 继续读取下一块数据
                    read();
                }).catch(error => {
                    reject(error);
                });
            }
            read();
        }).catch(error => {
            reject(error);
        });
    });
}
  • downloadWithProgress 函数接受四个参数:
    • url:欲下载资源的 URL。
    • loading_callback:下载进度回调函数,参数为下载进度百分比(精确到两位小数)和下载任务的 ID。
    • finished_callback:下载完成回调函数,参数为下载任务的 ID。
    • id:下载任务的 ID。
  • 函数返回一个 Promise,其中包含一个 Blob 对象,表示下载的内容。
  • 在函数体内部,通过 fetch 方法发送 HTTP 请求获取资源。
  • 通过 response.headers.get('Content-Length') 获取资源的总字节数。
  • 创建一个 reader 对象来读取响应体的数据。
  • 定义一个 chunks 数组,用于存储每个数据块。
  • 定义 downloadedBytes 变量来跟踪已下载的字节数。
  • 使用 reader.read() 方法逐块读取数据,并在每次读取完成后执行回调函数。
  • 在读取完成后,将所有数据块组合成一个 Blob 对象。
  • 调用 finished_callback 回调函数,通知下载任务已完成。
  • 最终,通过 resolve(blob) 返回包含下载内容的 Blob 对象。

使用举例:

// ...
let a = a_ref.current;

downloadWithProgress(data.url, showDownloadProgress, finished_callback, 1).then(blob => {
    a.href = URL.createObjectURL(blob);
    a.download = data.title + '.mp4';
    a.click();
});
// ...

Blob URL

可以使用URL.createObjectURL()同步函数将获得的Blob变量生成一个内存URL(Blob URL,生命周期仅限当前标签页的document)

createObjectURL返回一段带hash的url,并且一直存储在内存中,直到document触发了unload事件(例如:document close)或者执行revokeObjectURL来释放。」

然后可以将某个a标签的href属性设置为该URL,之后触发点击该a标签即可完成下载(从内存下载到磁盘)。

另外,回调函数搭配其他组件可以实现监控下载进度,反馈下载情况。

比如:

const showDownloadProgress = (progress, id) => {
    notify_loading(<span>正在下载视频: <span style={{ color: 'rgb(96, 165, 250)', width: '66px', display: 'inline-block', textAlign: 'right' }}>{progress}%</span></span>, 'downloading_video' + id);
};

const finished_callback = (id) => {
    toast.dismiss('downloading_video' + id);
    notify_success('视频下载完毕 !', 'finished_video' + id);
}

这里调用配置好的toast库的气泡通知组件,可以实时显示远程资源的下载进度以及完成下载的通知。

Web Share API 与 File类

Web Share API用来进行文件分享,打通系统级分享API,目前支持的浏览器较少,仅Mac端Safari和移动端的Safari、Chrome等浏览器支持。且只能通过用户的直接操作唤起,比如click点击事件。

File类是Blob类的子类,可以通过new File([blob], title)的形式创建(注意参数是Blob数组)。

let title = `P${item.index + 1} - ${item.title}.mp4`;
let file = new File([blob], title);
if (navigator.canShare && navigator.canShare({ files: [file] })) {
    navigator.share({
        files: [file],
        title: title,
        text: title,
    }).then(r => notify_success('视频' + (item.index + 1) + '分享成功 !', 'share_video_success' + (item.index + 1)))
        .catch(e => {
            console.log(e);
            notify_error('视频' + (item.index + 1) + '分享失败 !', 'share_video_error' + (item.index + 1));
        })
}

Data URL

通过FileReader.readAsDataURL(file)可以获取一段data:base64的字符串,FileReader.readAsDataURL()是异步执行(过一段时间)。

FileReader.readAsDataURL()返回包含很多字符的base64(URL本身包含了全部数据),会比blob url消耗更多内存,但是在不用的时候会自动从内存中清除(通过垃圾回收机制)。

如果不太在意设备性能问题,并想获取图片的base64,则推荐使用FileReader.readAsDataURL()

示例:

<input type="file" id="btn" accept="image/*" value="点击上传" />
<img id="img"/>
btn.addEventListener('change',function(){
    let file = this.files[0];
    // 进一步防止文件类型错误
    if(!/image\/\w+/.test(file.type)){
        alert("看清楚,这个需要图片!");
        return false;
    }
    img.src = URL.createObjectURL(file)
})

Blob URL和Data URL的区别

在JavaScript中,Blob URL和Data URL都是用来表示数据的特殊URL格式。

Blob URL是用于表示二进制数据的URL,可以通过URL.createObjectURL()方法创建。它接受一个Blob对象作为参数,并返回一个用于访问该Blob数据的URL。Blob URL通常用于在浏览器中显示或下载二进制文件,比如图片、音频、视频等。

以下是使用Blob URL的示例代码:

// 创建一个Blob对象
const blob = new Blob([data], { type: 'image/png' });

// 生成Blob URL
const blobURL = URL.createObjectURL(blob);

// 在img元素中显示图片
const img = document.createElement('img');
img.src = blobURL;
document.body.appendChild(img);

// 释放Blob URL
URL.revokeObjectURL(blobURL);

Data URL是一种用于表示数据的URL格式,可以直接包含数据内容,通常以data:开头。Data URL将数据编码为Base64格式,并包含数据的MIME类型,可以直接在URL中嵌入数据内容。Data URL适用于将小型数据嵌入到网页中,比如小图片或CSS背景图等。

以下是使用Data URL的示例代码:

// 将文本数据转换为Data URL
const data = 'Hello, World!';
const dataURL = `data:text/plain;charset=utf-8,${encodeURIComponent(data)}`;

// 在a标签中添加下载链接
const a = document.createElement('a');
a.href = dataURL;
a.download = 'text.txt';
a.textContent = 'Download';
document.body.appendChild(a);

请注意,Data URL适用于小型数据,对于大型数据(比如大图像或大文件),Data URL可能会导致性能问题和内存消耗过大,因此更适合用于小型数据的嵌入。而对于大型数据的处理,Blob URL通常更合适。

相关资料


A Student on the way to full stack of Web3.