封装 downloadFile 函数,从服务器下载文件

实现的功能

downFile.js 文件主要实现一个 downloadFile 函数,用于从服务器下载文件。该函数支持多种请求方法(如 getpostFormpostpostFile),并根据不同的请求方法构建请求配置。同时,函数会处理加载状态,根据不同浏览器处理文件下载,还能处理服务器返回的普通对象数据文件流数据

代码

js 复制代码
import axios from 'axios';
import qs from 'qs';
import { message } from 'antd';

export default function downloadFile(
  url,
  parmas,
  method = 'get',
  callBackData, // 回调
) {
  // 从响应头中提取文件名
  function getFilenameFromHeaders(headers) {
    const contentDisposition = headers['content-disposition'];
    if (!contentDisposition) return '';
    const [, name] = contentDisposition.split(';');
    const [, fileName] = name.split('filename=');
    return decodeURIComponent(fileName.trim());
  }
  
  // 该函数用于处理文件下载,根据浏览器类型(是否为 IE)采用不同的下载方式。该函数也可以提取为一个单独的文件,比如服务端返回的是json,某些情况才需要下载的时候,可以callBackData拿到数据去做一些判断之后再进行文件下载
  const download = (response) => {
    const filename = getFilenameFromHeaders(response.headers);
    const blob = new Blob([response.data]);
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveOrOpenBlob(blob, filename);
    } else {
      const downloadElement = document.createElement('a');
      const href = window.URL.createObjectURL(blob);
      downloadElement.href = href;
      downloadElement.download = filename;
      document.body.appendChild(downloadElement);
      downloadElement.click();
      document.body.removeChild(downloadElement);
      window.URL.revokeObjectURL(href);
    }
  };

  // 使用对象映射来返回不同请求的config参数
  const methodConfigMap = {
    get: (getUrl, getParmas) => ({
      method: 'get',
      url: `${getUrl}?${qs.stringify(getParmas)}`,
      responseType: 'blob',
    }),
    postForm: (getUrl, getParmas) => ({
      method: 'post',
      url: `${getUrl}`,
      data: qs.stringify(getParmas),
      responseType: 'blob',
    }),
    post: (getUrl, getParmas) => ({
      method: 'post',
      url: `${url}`,
      data: { ...getParmas },
      responseType: 'blob',
    }),
    postFile: (getUrl, getParmas) => ({
      method: 'post',
      url: `${url}`,
      data: getParmas,
      responseType: 'blob',
    }),
  };
  const config = methodConfigMap[method]?.(url, parmas) || {};
  axios(config)
    .then((response) => {
      const reader = new FileReader();
      reader.readAsText(response.data, 'utf-8');
      reader.onload = (e) => {
        try {
          // 是普通对象数据
          const jsonData = JSON.parse(e.target.result);
          callBackData && callBackData(jsonData);
          message.error(jsonData.message || jsonData.msg);
        } catch (err) {
          // 是流文件直接下载
          callBackData && callBackData();
          download(response);
        }
      };
    })
    .catch((err) => {
      message.error(err.message); 
    });
}

应用场景

比如业务中的批量导出,上传文件之后服务端返回给前端的结果为文件流(这里可能是上传结果异常数据)。

使用实例

  1. 直接通过服务端提供的url下载
js 复制代码
import downloadFile from '@utils/downFile';

    // 批量导出
    const batchExport = useCallback(() => {
      const url = 'api/export.do';
      // 有选中的数据导出选中的数据,没有选中的数据导出全部
      const param = { id, detailIds: selectedkeys.length === 0 ? '' : selectedkeys.join(',') };
      downloadFile(url, param, 'postForm');
    }, [dispatch, id, selectedkeys]);

2.上传文件之后,服务端直接返回一个数据流或者json,不另外提供下载接口

js 复制代码
  const callBackData = useCallback(
    (data) => {
      setImporting(false);
      closeFun();
      reloadPage();
      // 这里可以对data做一些判断,
      // 或者是前端把文件暂存到变量,展示一个结果弹窗,让用户点击的时候执行download函数并把暂存的文件流作为参数传递给download函数
      if (data?.code === 900) {
        message.success('导入成功');
      }
    },
    [closeFun, reloadPage],
  );

  // 导入(上传)
  const importFile = useCallback(() => {
    setImporting(true);
    const params = new FormData();
    params.append('file', fileInfo);
    downloadFile(
      'api/batchImportToPaid.do',
      params,
      'postFile',
      callBackData,
    );
  }, [callBackData, fileInfo]);
相关推荐
用户2873555325927 小时前
OpenResty 中 Lua 脚本实战示例(分场景讲清 Lua 解决了 Nginx 原生做不到的事)
面试
笨笨狗吞噬者7 小时前
VS Code 内置浏览器打开网页
前端
SamChan907 小时前
WebSocket实现PDF翻译进度实时推送:Go后端+React前端的完整方案
前端·websocket·pdf·c#·react·机器翻译
甲维斯7 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·人工智能
帅次7 小时前
Android 应用高级面试:Window 近1年高频追问 18 题
android·面试·职场和发展
奈斯先生Vector7 小时前
大模型 Agentic Workflow 架构解构:异构 API 调度与 Token 路由的多模态系统设计
开发语言·前端·架构·prompt·aigc·音视频
海带紫菜菠萝汤7 小时前
FFmpeg.wasm 实践:在浏览器中运行 FFmpeg 的能力边界与性能瓶颈
前端·javascript·ffmpeg·音视频·wasm
名字还没想好☜7 小时前
React 受控输入框光标跳到末尾:格式化输入时的 selection 丢失 bug 与修复
前端·javascript·react.js·bug·react·next.js
Access开发易登软件7 小时前
Access 怎么做前后端分离?用 Web API 读写 SQL Server
前端·数据库·人工智能·microsoft·excel·access
10share7 小时前
React 新一代样式隔离方案 —— 编译时、零运行时、原生写法
前端·react.js