vue+.netCore 下载导出文件

一、 下载

后端代码

/// <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

posted @ 2025-04-01 13:23  反骨少年  阅读(32)  评论(0)    收藏  举报