downloadjs、file-saver 和 js-file-download 都是用于在浏览器端触发文件下载的 JavaScript 库。它们的核心目标一致:解决原生 <a> 标签在处理 Blob 数据、跨域资源或特定 MIME 类型时的兼容性问题,并提供统一的 API 来保存文件到用户本地。
file-saver 是业界最老牌、使用最广泛的方案,它专注于实现 saveAs() 函数,对各种浏览器内核(包括旧版 IE 和移动端 Safari)有着极深的兼容性处理,是许多大型项目的默认选择。
downloadjs 提供了一个更简洁的函数式 API,它不仅支持 Blob,还能直接处理数据 URL (Data URLs) 和字符串,代码风格更加轻量,适合现代浏览器环境下的快速集成。
js-file-download 则是一个极度轻量的替代品,主要依赖创建临时 <a> 标签并模拟点击的方式,去除了复杂的兼容性 shim,适合只需要在现代浏览器中处理 Blob 下载的场景。
在前端开发中,触发文件下载看似简单,实则暗藏玄机。原生的 <a download> 属性在处理二进制流(Blob)、跨域资源或特定 MIME 类型时,往往在不同浏览器中表现不一。为了解决这些兼容性问题,downloadjs、file-saver 和 js-file-download 应运而生。它们都致力于将内存中的数据安全、可靠地保存为用户本地文件,但实现思路和适用场景各有不同。
这三个库最大的区别在于调用方式。理解它们的 API 形态,能帮你快速决定哪个更符合你的代码风格。
file-saver 导出一个名为 saveAs 的全局函数。它的设计非常明确:第一个参数是文件内容(通常是 Blob),第二个参数是文件名。这种命名方式语义清晰,但在模块化项目中需要显式导入。
// file-saver 用法
import { saveAs } from 'file-saver';
const blob = new Blob(["Hello World"], { type: "text/plain;charset=utf-8" });
// 明确调用 saveAs,语义为“保存为...”
saveAs(blob, "hello.txt");
downloadjs 采用单一的默认导出函数,通常命名为 download。它的参数顺序略有不同:先内容,再文件名,最后可选地指定 MIME 类型。这种设计让调用看起来更像是一个普通的工具函数。
// downloadjs 用法
import download from 'downloadjs';
const data = "Hello World";
// 直接调用 download,支持字符串、Blob 或 Data URL
download(data, "hello.txt", "text/plain");
js-file-download 同样导出一个默认函数,但其参数设计更加聚焦于 Blob 场景。它强制要求第一个参数必须是 Blob 对象,这使得它在处理非 Blob 数据时需要你先手动转换。
// js-file-download 用法
import fileDownload from 'js-file-download';
const blob = new Blob(["Hello World"], { type: "text/plain" });
// 必须传入 Blob,第二个参数是文件名
fileDownload(blob, "hello.txt");
在实际工程中,数据来源千奇百怪:可能是后端返回的 Blob,可能是 Base64 图片,也可能只是一段简单的 JSON 字符串。不同库对这些类型的支持程度决定了你的预处理工作量。
downloadjs 是三者中最灵活的。它内置了类型检测逻辑,可以直接接受字符串、Blob 对象甚至 Data URL。如果你有一段 Base64 编码的图片,可以直接传给它,无需手动转换为 Blob。
// downloadjs: 直接处理 Data URL
const base64Img = "data:image/png;base64,iVBORw0KG...";
download(base64Img, "image.png");
// 库内部自动处理转换,开发者省心
file-saver 主要设计用于处理 Blob 对象。虽然你也可以传字符串,但它通常会将其视为文本并自动包装成 Blob。对于复杂的二进制数据或 Data URL,最佳实践是手动创建 Blob 以确保 MIME 类型准确。
// file-saver: 推荐显式创建 Blob
const base64Img = "data:image/png;base64,iVBORw0KG...";
// 需要手动解析或转换,否则可能被视为纯文本
const byteString = atob(base64Img.split(',')[1]);
const mimeString = base64Img.split(',')[0].split(':')[1].split(';')[0];
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); }
const blob = new Blob([ab], { type: mimeString });
saveAs(blob, "image.png");
js-file-download 最为严格,它几乎完全依赖浏览器原生的 Blob URL 机制。如果传入非 Blob 数据,它可能无法按预期工作或需要你在外部转换。这使得它非常适合处理后端直接返回的文件流,但不适合处理原始字符串。
// js-file-download: 严格依赖 Blob
const jsonData = JSON.stringify({ user: "alice" });
// 必须先手动转换为 Blob
const blob = new Blob([jsonData], { type: "application/json" });
fileDownload(blob, "data.json");
这是选型时最关键的决策点。你的用户还在使用 IE11 吗?还是全员 Chrome/Firefox?
file-saver 是兼容性之王。它内部包含了大量的 Polyfill 逻辑,专门应对 IE10/11 的 msSaveBlob、旧版 Safari 的文件系统限制以及 Android 浏览器的各种怪癖。如果你的项目需要支持 5 年前的设备,它是唯一稳妥的选择。
// file-saver 内部逻辑示意(伪代码)
if (navigator.msSaveBlob) {
// IE 特有处理
navigator.msSaveBlob(blob, filename);
} else if (isSafariOldVersion) {
// 旧版 Safari 特殊处理
// ... 复杂的兼容性代码
} else {
// 现代浏览器标准处理
link.href = URL.createObjectURL(blob);
}
downloadjs 采取了折中方案。它支持大多数现代浏览器和部分旧浏览器,但在极端情况下(如非常老的 IE 版本)可能不如 file-saver 健壮。它的代码量适中,去除了一些极其罕见的兼容性代码,换取了更好的可维护性。
// downloadjs 处理逻辑相对简化
// 优先使用标准 a 标签下载,降级策略较少
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
js-file-download 完全拥抱现代标准。它假设运行环境支持 URL.createObjectURL 和 <a download> 属性。这意味着它在 IE11 及更低版本中可能无法工作。如果你的团队已经放弃了对旧浏览器的支持,这个库能为你节省宝贵的包体积。
// js-file-download 核心逻辑极其简单
// 仅依赖现代浏览器 API
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url); // 自动清理内存
处理大文件下载时,内存泄漏是一个潜在风险。这三个库在处理临时创建的 Object URL 时策略不同。
file-saver 和 js-file-download 都会在下载触发后尝试清理生成的 Object URL,防止内存堆积。file-saver 由于兼容性代码较多,其清理逻辑分散在不同的分支中,但总体是可靠的。
// file-saver 会在超时或点击后清理
setTimeout(function() {
URL.revokeObjectURL(url);
}, 60000); // 60 秒后清理
downloadjs 同样具备清理机制,但它的实现更加直接。对于大多数常规文件下载,这三者在内存管理上没有显著差异,除非你在极短时间内触发成千上万次下载。
// downloadjs 同样会在操作完成后释放 URL
// 内部实现确保了 revokeObjectURL 的调用
| 特性 | file-saver | downloadjs | js-file-download |
|---|---|---|---|
| 核心函数 | saveAs(blob, name) | download(data, name, type) | fileDownload(blob, name) |
| 数据支持 | 主要 Blob (可转字符串) | Blob, 字符串, Data URL | 仅 Blob |
| 兼容性 | 极强 (含 IE11, 旧 Safari) | 中等 (主流浏览器) | 弱 (仅现代浏览器) |
| 包体积 | 较大 (含 Polyfill) | 中等 | 极小 |
| 维护状态 | 稳定,长期维护 | 活跃 | 稳定 |
选择 file-saver 如果:
你正在维护一个面向公众的大型应用,用户设备五花八门,甚至可能包括几年前的旧电脑或安卓机。你需要“一次编写,到处运行”的确定性,不愿意因为浏览器差异去处理 Bug。它是企业级开发的保险单。
选择 downloadjs 如果:
你的项目主要面向现代浏览器,但偶尔需要处理一些简单的字符串或 Base64 数据,不想写额外的 Blob 转换代码。你喜欢简洁的函数式 API,希望代码读起来更像自然语言。它是全栈开发者和快速原型开发的首选。
选择 js-file-download 如果:
你明确知道你的用户群体只使用最新的 Chrome、Edge 或 Firefox(例如内部后台管理系统、SaaS 工具)。你对包体积非常敏感,希望依赖库越小越好。它是现代前端工程化的轻量级利器。
无论选择哪个,都要记住:在处理大文件时,始终关注浏览器的内存表现,并在下载完成后确保临时 URL 被正确释放。
如果你的项目主要面向现代浏览器,且希望 API 调用尽可能简洁(例如直接传入字符串或 Data URL),downloadjs 是极佳的选择。它的函数签名直观,无需实例化,适合在工具函数或小型项目中快速实现下载功能,但在处理极端老旧浏览器时可能不如 file-saver 稳健。
如果你需要构建企业级应用,必须兼容旧版 IE、旧版 Android 或处理复杂的 Blob 类型,file-saver 是首选。它是社区事实上的标准,经过了无数生产环境的验证,拥有最完善的兼容性兜底方案(如使用 FileSystem API 或 IE 特有的 blob 保存机制),适合对稳定性要求极高的场景。
如果你追求极致的轻量,且明确知道目标用户群体仅使用现代浏览器(Chrome, Firefox, Edge, Safari 新版),js-file-download 提供了最直接的解决方案。它没有多余的兼容性包袱,代码体积极小,适合对包体积敏感且不需要支持旧设备的项目。
========
The download() function is used to trigger a file download from JavaScript.
It specifies the contents and name of a new file placed in the browser's download directory. The input can be a URL, String, Blob, or Typed Array of data, or via a dataURL representing the file's data as base64 or url-encoded string. No matter the input format, download() saves a file using the specified file name and mime information in the same manner as a server using a Content-Disposition HTTP header.
npm install downloadjs
bower install downloadjs
require("downloadjs")(data, strFileName, strMimeType);
download function via <script> includedownload(data, strFileName, strMimeType);
require(['path/to/file'], function(download) {
download(data, strFileName, strMimeType);
});
download("hello world", "dlText.txt", "text/plain");
download("data:text/plain,hello%20world", "dlDataUrlText.txt", "text/plain");
download(new Blob(["hello world"]), "dlTextBlob.txt", "text/plain");
download("/robots.txt");
var str= "hello world", arr= new Uint8Array(str.length);
str.split("").forEach(function(a,b){
arr[b]=a.charCodeAt();
});
download( arr, "textUInt8Array.txt", "text/plain" );
download(document.documentElement.outerHTML, "dlHTML.html", "text/html");
download(new Blob(["hello world".bold()]), "dlHtmlBlob.html", "text/html");
(note that callback mode won't work on vanilla ajax or with binary files)
$.ajax({
url: "/download.html",
success: download.bind(true, "text/html", "dlAjaxCallback.html")
});
download("/diff6.png");
var x=new XMLHttpRequest();
x.open( "GET", "/diff6.png" , true);
x.responseType="blob";
x.onload= function(e){download(e.target.response, "awesomesauce.png", "image/png");};
x.send();
download.js works with a wide range of devices and browsers.
You can expect it to work for the vast majority of your users, with some common-sense limits:
Can I tell when a download is done/canceled? No.How can I style the temporary download link? Define CSS class styles for .download-js-link.What's up with Safari? I don't know either but pull requests that improve the situation are welcome.Why is my binary file corrupted? Likely: an incorrect MIME or using jQuery ajax, which has no bin support.How big of files work? Depends, try yourself: File Echo Demo... I do a 1GB dl routinely on a thinkpad...