文件导出的几种方式

1、返回文件流(二进制数据)

这是最常用的导出方式 ------ 后端实时生成文件(如 Excel、PDF),不落地存储,直接以二进制流形式返回给前端,前端接收后解析为可下载的文件。

js 复制代码
 
import axios from 'axios';

// 导出Excel接口请求
async function exportExcel() {
  try {
    const response = await axios({
      method: 'GET', // 也可用POST(传筛选参数时用POST)
      url: '/api/export/user-data', // 后端导出接口
      params: { status: 'active' }, // 筛选参数(可选)
      responseType: 'blob', // 关键:声明接收Blob二进制流
      headers: { 'Authorization': 'Bearer ' + token } // 权限头(可选)
    });

    // 1. 从响应头提取文件名(处理中文编码)
    const disposition = response.headers['content-disposition'];
    let filename = '默认文件名.xlsx';
    if (disposition) {
      // 匹配 filename*=UTF-8''xxx 格式(RFC 5987标准,支持中文)
      const match = disposition.match(/filename\*=UTF-8''([^;]+)/);
      if (match && match[1]) {
        filename = decodeURIComponent(match[1]); // 解码中文
      }
    }

    // 2. 转换Blob为下载URL
    const blob = new Blob([response.data], {
      // 手动指定MIME类型(与后端Content-Type一致,避免识别错误)
      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    });
    const downloadUrl = URL.createObjectURL(blob);

    // 3. 创建<a>标签触发下载
    const a = document.createElement('a');
    a.href = downloadUrl;
    a.download = filename; // 文件名(浏览器会忽略URL中的文件名,以这个为准)
    document.body.appendChild(a);
    a.click(); // 模拟点击下载

    // 4. 清理资源(避免内存泄漏)
    document.body.removeChild(a);
    URL.revokeObjectURL(downloadUrl); // 销毁临时URL
  } catch (error) {
    console.error('导出失败:', error);
    // 特殊处理:若后端返回JSON错误(如参数错误),需解析Blob为JSON
    if (error.response?.data instanceof Blob) {
      const errorText = await new Response(error.response.data).text();
      alert('导出失败:' + JSON.parse(errorText).message);
    }
  }
}

2、 文件下载链接(URL)

后端先将文件生成并存储在服务器 / 云存储(如 OSS、S3),再返回一个 临时有效的下载链接(含签名 / 过期时间,保证安全),前端直接通过链接触发下载。

js 复制代码
async function getExportUrl() {
  try {
    const response = await axios.get('/api/export/get-download-url', {
      params: { status: 'active' } // 筛选参数
    });
    const { downloadUrl, filename } = response.data.data;

    // 方式1:用<a>标签下载(推荐,可指定文件名)
    const a = document.createElement('a');
    a.href = downloadUrl;
    a.download = filename; // 强制指定文件名(若链接含文件名也可省略)
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);

    // 方式2:直接跳转(简单,但无法指定文件名,且可能触发浏览器预览)
    // window.location.href = downloadUrl;
  } catch (error) {
    console.error('获取下载链接失败:', error);
  }
}

//返回格式
 
{
  "code": 200,
  "message": "success",
  "data": {
    "downloadUrl": "https://xxx.com/files/export/user_123.xlsx?sign=abc123&expire=1717248000",
    "filename": "用户数据.xlsx",
    "expireTime": "2024-06-01 12:00:00"
  }
}
 

3、 后端返回「Base64 编码字符串」

后端将文件转换为 Base64 字符串(二进制→ASCII 字符),嵌入 JSON 返回给前端,前端解码后转换为文件。仅适合小文件(如 100KB 以内,Base64 会比原文件大 30%,大文件会导致请求体过大)。

js 复制代码
async function exportBase64File() {
  try {
    const response = await axios.get('/api/export/get-base64-file');
    const { base64Str, filename } = response.data.data;

    // 1. 解析Base64字符串(分离MIME类型和编码内容)
    const [meta, base64Data] = base64Str.split(',');
    const mime = meta.match(/:(.*?);/)[1]; // 提取MIME类型

    // 2. Base64解码为二进制数据(Uint8Array)
    const binaryStr = atob(base64Data); // atob解码Base64
    const len = binaryStr.length;
    const uint8Array = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
      uint8Array[i] = binaryStr.charCodeAt(i); // 转换为二进制数组
    }

    // 3. 转换为Blob并下载(后续逻辑与文件流一致)
    const blob = new Blob([uint8Array], { type: mime });
    const downloadUrl = URL.createObjectURL(blob);

    const a = document.createElement('a');
    a.href = downloadUrl;
    a.download = filename;
    document.body.appendChild(a);
    a.click();

    // 清理资源
    document.body.removeChild(a);
    URL.revokeObjectURL(downloadUrl);
  } catch (error) {
    console.error('导出失败:', error);
  }
}

//返回格式 
{
  "code": 200,
  "message": "success",
  "data": {
    "base64Str": "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,UEsDBBQABgAIAAAAIQ...",
    "filename": "用户数据.xlsx"
  }
} 
相关推荐
1024小神2 小时前
flutter 使用dio发送本地https请求报错
前端
正义的大古2 小时前
OpenLayers地图交互 -- 章节七:指针交互详解
前端·javascript·vue.js·openlayers
qwy7152292581632 小时前
vue自定义指令
前端·javascript·vue.js
niusir2 小时前
Zustand 实战:10 行代码搞定全局状态
前端·javascript·react.js
niusir2 小时前
React 状态管理的演进与最佳实践
前端·javascript·react.js
张愚歌2 小时前
快速上手Leaflet:轻松创建你的第一个交互地图
前端
唐某人丶2 小时前
教你如何用 JS 实现 Agent 系统(3)—— 借鉴 Cursor 的设计模式实现深度搜索
前端·人工智能·aigc
看到我请叫我铁锤2 小时前
vue3使用leaflet的时候高亮显示省市区
前端·javascript·vue.js
南囝coding2 小时前
Vercel 发布 AI Gateway 神器!可一键访问数百个模型,助力零门槛开发 AI 应用
前端·后端