1. 下载文件:这是最常见的需求,比如下载 PDF、图片、CSV、Excel 文档等。
  2. 下载代码:主要用于代码编辑器或在线 IDE,让用户可以下载当前编辑的代码文件。

下面我将详细介绍这两种场景下的常用方法和代码示例。

网页常用javascript下载
(图片来源网络,侵删)

下载文件

实现文件下载主要有三种主流方法,各有优缺点。

方法 1:使用 <a> 标签的 download 属性(最常用、最简单)

这是最推荐、最现代的方法,它利用了 HTML5 的标准属性,简单直接。

原理:创建一个 <a> 标签,将文件的 URL 设置为 href 属性,然后将 download 属性设置为你希望下载的文件名,当用户点击这个链接时,浏览器就会下载文件,而不是在新标签页中打开它。

优点

网页常用javascript下载
(图片来源网络,侵删)
  • 简单易用:代码量少,易于理解。
  • 无需服务器交互:可以直接下载任何同源或允许跨域的 URL 资源。
  • 原生支持:所有现代浏览器都支持。

缺点

  • 同源策略限制:如果文件 URL 与当前页面不同源,且服务器没有设置正确的 CORS(跨域资源共享)头,则会因为安全策略而失败。
  • 无法获取下载进度:这是一个“一次性”操作,无法监听下载过程中的状态(如开始、进度、完成、失败)。

代码示例:

/**
 * 使用 a 标签下载文件
 * @param {string} fileUrl - 文件的 URL
 * @param {string} fileName - 下载时显示的文件名
 */
function downloadFile(fileUrl, fileName) {
  // 创建一个临时的 a 标签
  const link = document.createElement('a');
  link.href = fileUrl;
  link.download = fileName || 'download'; // 如果没有提供文件名,则使用默认名
  // 将 a 标签添加到 body 中
  document.body.appendChild(link);
  // 模拟点击 a 标签
  link.click();
  // 点击后移除 a 标签,清理 DOM
  document.body.removeChild(link);
}
// --- 使用示例 ---
// 下载一个在线的图片
downloadFile('https://picsum.photos/800/600', 'random-image.jpg');
// 下载一个同目录下的 PDF
// downloadFile('/files/sample.pdf', 'my-document.pdf');

方法 2:使用 BlobURL.createObjectURL(最灵活,可处理动态数据)

当你需要下载由 JavaScript 动态生成的内容(如 JSON 数据、CSV 文件、Canvas 图像等)时,这个方法非常强大。

原理

网页常用javascript下载
(图片来源网络,侵删)
  1. 将你的数据(字符串、ArrayBuffer 等)转换成一个 Blob 对象。Blob 是一个表示二进制数据的文件对象。
  2. 使用 URL.createObjectURL() 方法为这个 Blob 对象创建一个临时的下载链接。
  3. 像方法 1 一样,创建一个 <a> 标签并触发点击。

优点

  • 灵活性极高:可以处理任何能在 JavaScript 中表示的数据。
  • 无需服务器:完全在前端生成文件并下载。
  • 可以处理大文件:通过 Blob 的分块处理,可以优化内存。

缺点

  • 代码比方法 1 稍复杂。
  • 同样无法获取原生下载进度。

代码示例:

/**
 * 使用 Blob 下载动态生成的内容
 * @param {string} content - 文件内容
 * @param {string} fileName - 下载时显示的文件名
 * @param {string} mimeType - 文件的 MIME 类型,如 'text/plain', 'application/json'
 */
function downloadWithBlob(content, fileName, mimeType) {
  // 1. 创建 Blob 对象
  const blob = new Blob([content], { type: mimeType });
  // 2. 创建临时下载链接
  const url = URL.createObjectURL(blob);
  // 3. 创建 a 标签并触发点击
  const link = document.createElement('a');
  link.href = url;
  link.download = fileName;
  document.body.appendChild(link);
  link.click();
  // 4. 清理:移除 a 标签并释放 URL 对象
  document.body.removeChild(link);
  URL.revokeObjectURL(url); // 释放内存,非常重要!
}
// --- 使用示例 ---
// 1. 下载 JSON 文件
const jsonData = { name: '张三', age: 30, city: '北京' };
downloadWithBlob(JSON.stringify(jsonData, null, 2), 'user-data.json', 'application/json');
// 2. 下载 CSV 文件
const csvData = '姓名,年龄,城市\n李四,25,上海\n王五,28,广州';
downloadWithBlob(csvData, 'users.csv', 'text/csv');
// 3. 下载 Canvas 生成的内容
// const canvas = document.getElementById('myCanvas');
// const canvasDataUrl = canvas.toDataURL('image/png');
// // 注意:这里需要将 Data URL 转换为 Blob
// fetch(canvasDataUrl)
//   .then(res => res.blob())
//   .then(blob => {
//     const url = URL.createObjectURL(blob);
//     const link = document.createElement('a');
//     link.href = url;
//     link.download = 'canvas-image.png';
//     link.click();
//     URL.revokeObjectURL(url);
//   });

方法 3:通过后端 API 下载(最可靠,适合需要权限或大文件)

当文件需要用户登录后才能访问、文件非常大或者需要复杂的业务逻辑(如数据库查询后生成)时,必须通过后端 API 来处理。

原理

  1. 前端 JavaScript 发起一个网络请求(通常是 GETPOST)到后端的一个 API 端点。
  2. 后端处理请求,从数据库或文件系统读取文件,并将其作为 HTTP 响应体返回。
  3. 后端需要在响应头中设置 Content-Disposition: attachment; filename="...",这会告诉浏览器这是一个需要下载的附件。
  4. 前端接收到这个响应后,可以创建一个 <a> 标签,将 href 设置为这个 API 的 URL,并触发点击。

优点

  • 安全性高:可以验证用户身份,确保只有授权用户才能下载。
  • 适合大文件:后端有成熟的流式传输机制,可以避免一次性将大文件加载到内存。
  • 功能强大:可以在后端进行任何复杂的处理。

缺点

  • 需要后端配合:必须有一个后端服务来处理文件。
  • 用户体验稍差:需要一个网络请求,速度取决于服务器和用户网络。

前端代码示例:

/**
 * 通过 API 下载文件
 * @param {string} apiUrl - 后端 API 的 URL
 * @param {string} fileName - 下载时显示的文件名 (可选,后端也可以提供)
 */
function downloadViaApi(apiUrl, fileName) {
  // 创建一个临时的 a 标签
  const link = document.createElement('a');
  link.href = apiUrl;
  // 如果后端没有在响应头中提供文件名,可以在这里指定一个默认的
  // 但最好让后端通过 Content-Disposition 头来提供
  if (fileName) {
    link.download = fileName;
  }
  // 触发点击
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}
// --- 使用示例 ---
// 假设后端有一个 /api/export/invoices 接口,用于导出发票
// downloadViaApi('/api/export/invoices', 'invoices-2025.zip');

后端(Node.js/Express)代码示例:

const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
app.get('/api/download/sample', (req, res) => {
  const filePath = path.join(__dirname, 'files', 'sample.pdf');
  const fileName = 'sample-document.pdf';
  // 检查文件是否存在
  if (fs.existsSync(filePath)) {
    // 设置响应头,告诉浏览器这是一个附件,并指定文件名
    res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
    res.setHeader('Content-Type', 'application/pdf'); // 设置正确的 MIME 类型
    // 使用流来发送文件,避免内存溢出
    const fileStream = fs.createReadStream(filePath);
    fileStream.pipe(res);
  } else {
    res.status(404).send('File not found');
  }
});
app.listen(3000, () => console.log('Server running on port 3000'));

下载代码

这通常用于在线代码编辑器(如 CodePen, JSFiddle, StackBlitz),实现逻辑和 方法 2(Blob) 非常相似,因为代码内容是动态生成的。

核心逻辑

  1. 获取代码编辑器中的文本内容。
  2. 确定文件的类型(如 .js, .html, .css)。
  3. 使用 BlobURL.createObjectURL 创建下载链接并触发下载。

代码示例(以一个简单的文本域为例):

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">代码下载示例</title>
</head>
<body>
  <h2>在线代码编辑器</h2>
  <textarea id="code-editor" style="width: 100%; height: 200px; font-family: monospace;">
// 这是一个 JavaScript 示例
function hello() {
  console.log('Hello, World!');
}
hello();
  </textarea>
  <div>
    <button onclick="downloadCode('js')">下载 .js 文件</button>
    <button onclick="downloadCode('html')">下载 .html 文件</button>
    <button onclick="downloadCode('css')">下载 .css 文件</button>
  </div>
  <script>
    function downloadCode(fileType) {
      const editor = document.getElementById('code-editor');
      const content = editor.value;
      let mimeType, fileExtension;
      switch(fileType) {
        case 'js':
          mimeType = 'application/javascript';
          fileExtension = 'js';
          break;
        case 'html':
          mimeType = 'text/html';
          fileExtension = 'html';
          // 为了演示,我们简单地把 JS 代码包裹进 HTML 结构中
          content = `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">My Code</title>
</head>
<body>
    <script>
${content}
    </script>
</body>
</html>`;
          break;
        case 'css':
          mimeType = 'text/css';
          fileExtension = 'css';
          break;
        default:
          mimeType = 'text/plain';
          fileExtension = 'txt';
      }
      const fileName = `my-code.${fileExtension}`;
      // 使用 Blob 方法
      const blob = new Blob([content], { type: mimeType });
      const url = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.download = fileName;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      URL.revokeObjectURL(url);
    }
  </script>
</body>
</html>

总结与选择建议

方法 适用场景 优点 缺点
<a> 标签 download 下载静态文件(图片、PDF等),文件与页面同源或已配置 CORS。 最简单,无需服务器交互,原生支持。 受 CORS 限制,无法获取下载进度。
Blob + createObjectURL 下载动态生成的内容(JSON, CSV, Canvas 图像等)。 最灵活,完全前端实现,可处理任何数据。 代码稍复杂,无法获取原生下载进度。
后端 API 下载需要权限、大文件或复杂逻辑生成的文件 最安全可靠,适合企业级应用。 依赖后端,用户体验稍差。
Blob (代码下载) 在线代码编辑器,下载动态编辑的代码。 灵活,用户体验好。 与 Blob 方法 2 相同。

选择建议:

  • 如果只是下载一个静态文件,直接用 方法 1
  • 如果文件内容是 JS 动态生成的,用 方法 2
  • 如果文件涉及用户登录、权限或数据查询,用 方法 3
  • 如果是下载代码编辑器里的内容,用 方法 2 的变种。