惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Blog of Author Tim Ferriss
Know Your Adversary
Know Your Adversary
P
Palo Alto Networks Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
K
Kaspersky official blog
L
LINUX DO - 热门话题
P
Proofpoint News Feed
P
Privacy & Cybersecurity Law Blog
Google DeepMind News
Google DeepMind News
Attack and Defense Labs
Attack and Defense Labs
Cisco Talos Blog
Cisco Talos Blog
AI
AI
L
LINUX DO - 最新话题
H
Heimdal Security Blog
Hacker News: Ask HN
Hacker News: Ask HN
Webroot Blog
Webroot Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The GitHub Blog
The GitHub Blog
I
Intezer
Blog — PlanetScale
Blog — PlanetScale
有赞技术团队
有赞技术团队
S
Securelist
博客园_首页
IT之家
IT之家
Schneier on Security
Schneier on Security
博客园 - 叶小钗
罗磊的独立博客
WordPress大学
WordPress大学
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
MongoDB | Blog
MongoDB | Blog
P
Proofpoint News Feed
阮一峰的网络日志
阮一峰的网络日志
A
Arctic Wolf
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
W
WeLiveSecurity
The Register - Security
The Register - Security
D
DataBreaches.Net
S
Security @ Cisco Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
Recorded Future
Recorded Future
NISL@THU
NISL@THU
N
News and Events Feed by Topic
T
Tailwind CSS Blog
N
News and Events Feed by Topic
Cyberwarzone
Cyberwarzone
T
Tor Project blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com

博客园 - 反骨少年

cursor使用 wpf 进度条 https://docs.ultralytics.com/zh/tasks/detect/#export 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 vue 前端导出excel 通过outxml 复制sheet ,不完整 SVN提交过滤忽略的文件 博文阅读密码验证 - 博客园 OpenTelemetry 博文阅读密码验证 - 博客园 vue3+ElementPlus 后台布局搭建 IdentityServer4 canal管理 数据同步 Canal数据同步Kafka Xcode的思想,以及能让我学习的规范 阿里云的CICD
vue+.netCore 下载导出文件
反骨少年 · 2025-04-01 · via 博客园 - 反骨少年

一、 下载

后端代码

/// <summary>
/// 异步导出投诉报告
/// </summary>
/// <param name="exportParams">投诉报告查询参数</param>
/// <returns>文件流响应</returns>
[Route("/qms/complain/export/report")]
[HttpPost]
[Core.Filter.IgnoreActionLog] // 忽略日志记录
public async Task<IActionResult> ExportComplaintReportAsync([FromBody] ComplaintReportDto exportParams)
{
    // 获取文件流和文件名
    var complaintReportService = new ComplaintReportService(GetUserDBConnection());
    var userName = GetUserName();
    var (fileStream, fileName) = await complaintReportService.ExportComplaintReportAsync(exportParams, userName);

    // 重置文件流位置
    fileStream.Position = 0;

    // 允许前端获取 Content-Disposition 响应头
    Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");

    // 返回文件流
    return File(fileStream, "application/octet-stream", fileName);
}

前端代码

/**
 * 通用文件下载方法
 * 
 * @param {string} url 请求地址
 * @param {object} params 请求参数
 * @param {object} options 配置选项,可选(method, headers, responseType)
 * @returns {Promise} 返回完整响应体(包含文件流)
 * 
 * 示例:
 * downloadFile('/api/download', { id: 1 })
 *   .then(response => {
 *     // 处理下载的文件流
 *   })
 */
export function downloadFile(url, params, options = {}) {
  const {
    method = 'POST',
    headers = {
      'Content-Type': 'application/json'
    },
    responseType = 'blob',
  } = options;

  const token = getAccessToken() || getToken();

  // 创建独立的axios实例,避免全局拦截器干扰
  const instance = axios.create({
    baseURL: process.env.VUE_APP_BASE_API,
    timeout: 300000,
    headers: {
      ...headers,
      ...(token ? { 'Authorization': 'Bearer ' + token } : {})
    },
    responseType,
  });

  const config = {
    url,
    method,
  };

  if (method.toUpperCase() === 'GET') {
    config.params = params;
  } else {
    config.data = params;
  }

  return instance.request(config)
    .then(response => {
      return response; // 返回完整响应体,包含 headers、status、data
    })
    .catch(error => {
      return Promise.reject(error);
    });
}

 调用下载

    /**
     * 下载文件
     * @param {Object} response 响应对象
     */
    downloadFile(response) {
      const contentDisposition = response.headers['content-disposition'] || '';
      const filenameRegex = /filename\*=UTF-8''([^;]+)/;
      const match = filenameRegex.exec(contentDisposition);

      if (!match) {
        console.error('未获取到文件名,下载中止');
        return;
      }

      const filename = decodeURIComponent(match[1]);
      const blob = response.data;
      const url = URL.createObjectURL(blob);

      const link = document.createElement('a');
      link.href = url;
      link.download = filename;
      link.style.display = 'none';
      document.body.appendChild(link);

      link.click();
      URL.revokeObjectURL(url);
      document.body.removeChild(link);
    },
/**
 * 通过POST请求获取文件
 * 
 * 使用POST方法向文件接口提交查询参数,获取文件流(适用于参数较多或复杂的场景)
 * 
 * @param {Object} queryData - POST请求的查询参数对象
 * @returns {Promise<Blob>} 返回文件流的Promise对象,resolved值为Blob类型
 * 
 * 示例:
 * getFileByPost({ id: '123', type: 'report' })
 *   .then(response => {
 *     // 处理文件流(预览或下载)
 *     handleFileResponse(response);
 *   })
 */
export function getFileByPost(queryData) {
  // 显式指定method为POST,与函数功能保持一致
  return downloadFile('/wms/file/getfile', queryData, { method: 'POST' });
}

/**
 * 下载文件(GET方式)
 * 
 * 使用GET方法向文件接口传递查询参数,获取文件流
 * 适用于参数简单、长度有限的场景
 * 
 * @param {Object} queryParams - GET请求的查询参数对象(会转为URL查询字符串)
 * @returns {Promise<Blob>} 返回文件流的Promise对象
 * 
 * 示例:
 * getFileByGet({ fileId: '123', type: 'preview' })
 *   .then(response => {
 *     // 处理文件流(预览或下载)
 *   })
 */
export function getFileByGet(queryParams) {
  // 显式指定method为GET,参数会自动作为URL查询参数
  return downloadFile('/wms/file/getfile', queryParams, { method: 'GET' });
}

 接口调用

https://blog.51cto.com/u_16213413/12422025

https://blog.csdn.net/weixin_43118088/article/details/128852084