下载文件是上网的一个重要方面。每天从 Internet 下载大量文件,从二进制文件(如应用程序、图像、视频和音频)到纯文本文件。
Web 开发人员想将此功能添加到应用程序中,那么可以按照以下方法进行操作。
我们将检查 3 种不同的方法:
- 仅使用 HTML 元素的基本模式;
- 使用带有 Fetch API 和 HTML 元素的 JavaScript;
- 使用 XMLHttpRequest 和 HTML 元素,但在更复杂的场景中,我们实施一个系统来衡量进度;
方法一
第一种也是最简单的方法意味着创建一个具有 download 属性的 a 标签。
根据定义,download 属性指定当用户单击超链接时将下载目标(href 属性中指定的文件)。
此外,使用此下载属性,我们可以在下载文件后指定文件的新名称。因此,如果我们想下载具有特定名称的文件,我们可以使用此属性来控制它。但是,当本机下载窗口出现时,用户仍然可以更改名称,但我们提供的名称将是默认名称。
如果省略该值,则使用原始文件名。
<a href="https://t-bucket.sfo3.digitaloceanspaces.com/100mb.txt" download>Download 100mb.txt</a>
当我们不需要根据这个下载过程做任何操作时,这个方法就很棒。同时,即使我们无法使用 JavaScript 呈现 a 标签,我们也可以使用此方法:
function downloadUsingAnchorElement() {
const anchor = document.createElement('a');
anchor.href = IMG_URL;
anchor.download = FILE_NAME;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
}
上面的函数做同样的事情,只是我们动态创建 a 标签,仅用于此下载操作,然后我们将其删除。
IMG_URL 是我们要下载的图片的URL;
FILE_NAME 为文件下载后的新名称;
此方法的局限性在于它必须遵循同源策略,因此该属性适用于同源 URL。
一个常见的场景是,当你想从另一台服务器下载图像时,浏览器没有下载它,而是在新选项卡中打开它。
此方法的关键方面是下载过程将自动开始,并且可以在浏览器中本地查看。
方法二
第二种方法和第三种方法使用与 a 标签相同的技术,但我们不提供图像 URL,而是将文件的内容转换为 Blob,然后使用 createObjectURL 方法从中创建 DOMString。
async function downloadUsingFetch() {
const image = await fetch(IMG_URL);
const imageBlog = await image.blob();
const imageURL = URL.createObjectURL(imageBlog);
const anchor = document.createElement('a');
anchor.href = imageURL;
anchor.download = FILE_NAME;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
URL.revokeObjectURL(imageURL);
}
请注意,最后我们使用了 URL.revokeObjectURL ,这在内存管理方面很重要。使用 URL.createObjectURL 时会创建一个新的对象 URL,即使它是使用相同的 blob 对象调用的。
无论何时创建对象 URL,它都会在创建它的文档的整个生命周期内保持不变。浏览器将在卸载文档时释放所有对象 URL。但是,重要的是在不再需要对象 URL 时释放它们,以提高性能并最大限度地减少内存使用。
此方法的关键方面是下载过程将自动开始,但在我们的应用程序内,只有在下载完成后才会传递给浏览器。
请注意,在上面的 GIF 中,一旦我们单击“下载”按钮,似乎什么也没有发生,因为下载在我们的应用程序中作为异步任务发生,一旦完成,它将传递给浏览器。
一旦该浏览器窗口出现并且我们单击保存,该文件将自动保存在我们的计算机上。
使用这种方法,我们现在可以下载任何类型的文件,而不管源服务器是什么。然而,问题是因为下载发生在我们的应用程序内部,用户可能认为他点击时什么也没发生,因此我们需要通过实施进度测量来管理大文件下载。
同时,当我们需要在文件下载完成后在应用程序中执行某些操作时,此方法很有用。显示一条消息,向后端发送请求渲染一个新页面,等等......
方法三
第三种方法与第二种方法类似,我们仍将使用 Blob 和 createObjectURL,但我们将使用 XMLHttpRequest 而不是使用 Fetch API。
我们使用 XMLHttpRequest 而不是 Fetch,因为目前 Fetch API 不提供进度测量接口,而 XMLHttpRequest 提供。
function downloadWithProgress() {
const startTime = new Date().getTime();
request = new XMLHttpRequest();
request.responseType = "blob";
request.open("get", IMG_URL, true);
request.send();
request.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
const imageURL = window.URL.createObjectURL(this.response);
const anchor = document.createElement("a");
anchor.href = imageURL;
anchor.download = FILE_NAME;
document.body.appendChild(anchor);
anchor.click();
}
};
request.onprogress = function (e) {
const percent_complete = Math.floor((e.loaded / e.total) * 100);
const duration = (new Date().getTime() - startTime) / 1000;
const bps = e.loaded / duration;
const kbps = Math.floor(bps / 1024);
const time = (e.total - e.loaded) / bps;
const seconds = Math.floor(time % 60);
const minutes = Math.floor(time / 60);
console.log(
`${percent_complete}% - ${kbps} Kbps - ${minutes} min ${seconds} sec remaining`
);
}
}
开始和 onreadystatechange 块类似于第二个函数。将响应下载为 Blob 对象,创建 DOMString,并使用锚元素下载文件。
Insideonprogress 我们使用 e.loaded 和 e.total 值来计算进度的百分比和经过的时间,以及下载速度和剩余时间。
请注意,在上面的 GIF 中,我们的行为与第二种方法相同,只是现在我们可以监控进度。文件下载完成后,将被发送到浏览器,然后立即保存到磁盘。
总结
上述每个方法都代表了对前一个方法的更新。
第一种方法是最简单的。在此,我们只是将下载过程转发给浏览器以在本地进行管理。当应用程序不必根据加载状态执行某些操作时,此方法是首选方法。
在第二种情况下,我们在内部管理下载并仅在下载完成后才将其发送到浏览器。通过这种方式,我们可以控制应用程序内部的下载,我们可以根据其状态做出反应。此方法适用于快速下载的小文件,但当文件太大时,如果 UI 上没有任何提示用户正在下载,用户可能会认为应用程序有问题。
在最后一个方法中,我们实现了自己的进度测量,这与浏览器中的类似。
Top comments (0)