LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

【Web开发】前端实现文件下载的N种方式

admin
2025年12月11日 22:53 本文热度 5
在日常开发中,我们经常需要实现文件下载功能。不同的业务场景可能需要不同的实现方式,这里整理了几种实用的方法,希望能帮助你解决实际问题。

方式一:使用a标签的download属性

这是最简单直接的下载方式,适合静态文件或已知URL的情况:

<!-- 基础用法 --><a href="/files/report.pdf" download>下载报告</a>
<!-- 指定下载文件名 --><a href="/files/data.xlsx" download="2024年销售数据.xlsx">下载Excel文件</a>
<!-- 配合JavaScript使用 --><button onclick="downloadStaticFile()">下载图片</button>
<script>function downloadStaticFile() {  const link = document.createElement('a');  link.href = '/images/photo.jpg';  link.download = '我的照片.jpg';  link.click();}</script>

适用场景

  • 下载项目中的静态资源

  • 下载同源服务器上的文件

  • 需要简单快速实现下载功能

注意事项

  • 跨域资源可能无法正常下载

  • 无法添加自定义请求头

  • 部分浏览器对文件名有字符限制

方式二:通过页面跳转下载

这种方法通过改变页面地址或打开新窗口来触发下载:

// 当前页面跳转function downloadByRedirect() {  // 后端需要设置Content-Disposition响应头  window.location.href = '/api/download/file/123';}
// 新窗口打开function downloadByNewWindow() {  window.open('/api/export/pdf''_blank');}
// 带参数的下载function downloadWithParams() {  const params = new URLSearchParams({    startDate'2024-01-01',    endDate'2024-12-31'  });
  window.location.href = `/api/report/export?${params}`;}

适用场景

  • 后端动态生成的文件

  • 需要传递参数给后端

  • 跨域下载(需服务器支持)

注意事项

  • 文件名由后端通过Content-Disposition响应头控制

  • 可能被浏览器弹出拦截器阻止

  • 无法获取下载进度

方式三:使用Fetch API和Blob对象

对于需要认证、进度显示或复杂处理的场景,这是最灵活的方式:

// 基本实现async function downloadWithFetch() {  try {    // 发送请求    const response = await fetch('/api/files/download', {      headers: {        'Authorization''Bearer ' + getToken()      }    });
    // 获取Blob数据    const blob = await response.blob();
    // 创建下载链接    const url = window.URL.createObjectURL(blob);    const link = document.createElement('a');    link.href = url;
    // 从响应头获取文件名,或使用默认名    const filename = getFilenameFromResponse(response) || 'download';    link.download = filename;
    // 触发下载    document.body.appendChild(link);    link.click();
    // 清理资源    document.body.removeChild(link);    window.URL.revokeObjectURL(url);  } catch (error) {    console.error('下载失败:', error);    alert('文件下载失败,请重试');  }}
// 从响应头提取文件名function getFilenameFromResponse(response) {  const disposition = response.headers.get('Content-Disposition');  if (disposition && disposition.includes('filename=')) {    let filename = disposition.split('filename=')[1];    // 去除可能的分号和引号    filename = filename.replace(/['";]/g'');    return decodeURIComponent(filename);  }  return null;}
支持下载进度的版本
function downloadWithProgress(url, onProgress) {  return new Promise((resolve, reject) => {    const xhr = new XMLHttpRequest();    xhr.open('GET', url, true);    xhr.responseType = 'blob';
    // 监听进度事件    xhr.addEventListener('progress'(event) => {      if (event.lengthComputable && onProgress) {        const percent = Math.round((event.loaded / event.total) * 100);        onProgress(percent);      }    });
    xhr.onload = function() {      if (xhr.status === 200) {        const blob = xhr.response;        const url = window.URL.createObjectURL(blob);
        // 从响应头获取文件名        const disposition = xhr.getResponseHeader('Content-Disposition');        let filename = 'download';        if (disposition) {          const match = disposition.match(/filename="?(.+)"?/);          if (match) filename = match[1];        }
        // 创建并触发下载        const link = document.createElement('a');        link.href = url;        link.download = filename;        document.body.appendChild(link);        link.click();        document.body.removeChild(link);        window.URL.revokeObjectURL(url);
        resolve();      } else {        reject(new Error(`下载失败: ${xhr.status}`));      }    };
    xhr.onerror = reject;    xhr.send();  });}
// 使用示例downloadWithProgress('/api/large-file'(percent) => {  console.log(`下载进度: ${percent}%`);  // 可以更新UI进度条});

方式四:下载前端生成的内容

有时候我们需要下载前端生成的数据,比如Canvas图片或JSON数据:

// 下载Canvas图片function downloadCanvasImage(canvasElement, filename = 'image.png') {  canvasElement.toBlob((blob) => {    const url = URL.createObjectURL(blob);    const link = document.createElement('a');    link.href = url;    link.download = filename;    link.click();    URL.revokeObjectURL(url);  });}
// 下载JSON数据function downloadJSON(data, filename = 'data.json') {  const jsonStr = JSON.stringify(data, null2);  const blob = new Blob([jsonStr], { type'application/json' });  const url = URL.createObjectURL(blob);  const link = document.createElement('a');  link.href = url;  link.download = filename;  link.click();  URL.revokeObjectURL(url);}
// 下载CSV数据function downloadCSV(data, filename = 'data.csv') {  let csvContent = '';
  // 添加表头  if (data.length > 0) {    const headers = Object.keys(data[0]);    csvContent += headers.join(',') + '\n';  }
  // 添加数据行  data.forEach(row => {    const values = Object.values(row).map(value => {      // 处理包含逗号或引号的值      if (typeof value === 'string' && (value.includes(',') || value.includes('"'))) {        return `"${value.replace(/"/g, '""')}"`;      }      return value;    });    csvContent += values.join(',') + '\n';  });
  const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });  const url = URL.createObjectURL(blob);  const link = document.createElement('a');  link.href = url;  link.download = filename;  link.click();  URL.revokeObjectURL(url);}

实用技巧和注意事项

  1. 文件类型检测

function getDownloadFilename(response, defaultName = 'file') {  const disposition = response.headers.get('Content-Disposition');  let filename = defaultName;
  if (disposition) {    const matches = disposition.match(/filename\*?=["']?(?:UTF-8'')?([^"';]+)["']?/i);    if (matches && matches[1]) {      filename = decodeURIComponent(matches[1]);    }  }
  return filename;}

  1. 错误处理和重试

async function downloadWithRetry(url, maxRetries = 3) {  for (let i = 0; i < maxRetries; i++) {    try {      await downloadWithFetch(url);      return// 成功则退出    } catch (error) {      console.warn(`第${i + 1}次下载失败:`, error);      if (i === maxRetries - 1) {        throw error; // 最后一次失败则抛出异常      }      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // 延迟重试    }  }}

  1. 大文件分块下载
    对于非常大的文件,可以考虑使用流式下载或分块下载,避免内存占用过高。

总结

不同的下载方式适用于不同的场景:

  • 简单静态文件:直接使用a标签

  • 后端生成文件:使用页面跳转或Fetch API

  • 需要认证或进度显示:使用Fetch/XMLHttpRequest

  • 前端生成内容:使用Blob和Object URL

选择哪种方式,主要看你的具体需求。如果只是下载一个静态图片,用最简单的a标签就好;如果需要下载需要认证的大文件,那么使用Fetch API配合进度显示会更合适。

在实际开发中,建议封装一个通用的下载函数,根据不同的参数选择合适的下载方式,这样既能保证代码复用,也能灵活应对各种需求。


阅读原文:原文链接


该文章在 2025/12/12 9:23:02 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved