导出文件,能够导出但是文件打不开

ops/2025/1/12 14:08:10/

背景:

在项目开发中,对于列表的查询,而后会有导出功能,这里导出的是一个excell表格。实现了两种,1.导出的文件,命名是前端传输过去的;2.导出的文件,命名是根据后端返回的文件名获取的。

 //解码获取导出文件名

        const fileNames = res.headers['content-disposition']

        const fileName = decodeURIComponent(fileNames.match(/=(.*)$/)[1])

//能够导出但是打不开,导出接口的网络请求要带上responseType: 'blob',

     //导出文件【get】

    exportCrewInfoFile(params) {

        return request.Get("/data/ferryShip/download?", params, {

            headers: {

                "Content-Type": "application/json",

            },

            responseType: 'blob',

        });

    },

    //导出文件【post】

    exportWarningFile(params) {

        return request.Post("/data/warningRecord/download", params, {

            headers: {

                "Content-Type": "application/json",

            },

            responseType: 'blob',

        });

    },

前端界面:

前端请求接口,axios发起网络请求:

axios封装:

javascript">import axios from "axios";
import useShoreBasedStore from "@/store";
import qs from 'qs';
import router from "@/router/index.js";
import { ElMessageBox } from "element-plus";let messageBoxVisible = false
export const BASEUrl = import.meta.env.VITE_USER_NODE_ENV === 'development' ? '/apiproxy/pa' : import.meta.env.VITE_API_URL + '/pa'
const request = axios.create({baseURL: BASEUrl,timeout: 3000 * 60,headers: { "Content-Type": "application/x-www-form-urlencoded" },
});/*** 请求拦截添加token* */
request.interceptors.request.use((config) => {const user = useShoreBasedStore();if (user.userInfo.token) {config.headers.Authorization = user.userInfo.token;}return config;
}, (error) => {return Promise.reject(error)
});const showMsg = () => {if (!messageBoxVisible) {messageBoxVisible = trueconst user = useShoreBasedStore()ElMessageBox.confirm('您的登录信息已过期,请重新登录!','温馨提示',{confirmButtonText: '确认',cancelButtonText: '取消',type: 'warning'}).then(() => {user.loginOut()router.push({ name: 'login' })}).catch(() => {}).finally(() => {messageBoxVisible = false})}
}request.interceptors.response.use((response) => {const data = response.dataif (data.status === 401 || data.code === 401) {showMsg()} else {return response}
}, (error) => {if (error.response.data.code === 401) {showMsg()} else {return Promise.reject(error)}
})// 定义get方法
request.Get = function (url, params, config) {if (params) {return request.get(url + qs.stringify(params), config);}return request.get(url, config);
};// 定义post方式
request.Post = function (url, params, config) {if (params) {return request.post(url, params, config);}return request.post(url, config);
};export default request;

接口:

javascript">import request from "../utils/request.js";const fileApi = {// 上传文件uploadFile(params) {return request.Post("/data/file/upload", params, {headers: {"Content-Type": "multipart/form-data",},});},// 删除文件deleteFile(params) {return request.Post("/data/file/delete", params, {headers: {"Content-Type": "application/json",},});},//导出文件【get】exportFerryPortFile(params) {return request.Get("/data/ferryPort/download?", params, {headers: {"Content-Type": "application/json",},responseType: 'blob',});},//导出文件【post】exportWarningFile(params) {return request.Post("/data/warningRecord/download", params, {headers: {"Content-Type": "application/json",},responseType: 'blob',});},
}
export default fileApi;

导出封装的方法: 

javascript">/**** @param {*} fileContent 文件本体* @param {*} _fileName 自定义文件名*/
export const exportFileUtil = (fileContent, _fileName) => {const content = fileContent;const blob = new Blob([content], {type: fileContent.type || "application/octet-stream; charset=utf-8",});const fileName = _fileName;if ("download" in document.createElement("a")) {//非IE下载const a = document.createElement("a"); //创建一个a标签a.download = fileName; //指定文件名称a.style.display = "none"; //页面隐藏a.href = URL.createObjectURL(blob); // href用于下载地址document.body.appendChild(a); //插到页面上a.click(); //通过点击触发URL.revokeObjectURL(a.href); //释放URL 对象document.body.removeChild(a); //删掉a标签} else {//IE10 + 下载navigator.msSaveBlob(blob, fileName);}
};
/*** * @param {*} res 接口返回的文件流*/
export const dowloadFileUrl = (res) => {const fileNames = res.headers['content-disposition']if (fileNames) {//解码const fileName = decodeURIComponent(fileNames.match(/=(.*)$/)[1])// 处理返回的文件流const content = res.dataconst blob = new Blob([content], {type: res.data.type||"application/vnd.ms-excel"});if ('download' in document.createElement('a')) {//非IE下载const a = document.createElement('a') //创建一个a标签a.download = fileName //指定文件名称a.style.display = 'none' //页面隐藏a.href = URL.createObjectURL(blob) // href用于下载地址document.body.appendChild(a) //插到页面上a.click() //通过点击触发URL.revokeObjectURL(a.href) //释放URL 对象document.body.removeChild(a) //删掉a标签} else {//IE10 + 下载navigator.msSaveBlob(blob, fileName)}}
}

使用导出的方法:

javascript">//只是举例,根据实际进行调整
const exportExcel = () => {const search = searchForm.value.submitData();const params = {...search,};api.fileApi.exportCrewInfoFile(params).then((res) => {if (res.status === 200) {exportFileUtil(res.data, "渡口管理导出文件.xlsx");dowloadFileUrl(res)}}).finally((err) => {console.log(err);});
};

写到这儿,就实现了导出功能。。。下面的是导出接口的详细解释: 

 

一、导出文件使用get请求

(1)、导出文件,get请求里面传参有数组等复杂数据结构

前端界面:

上图可以看见post查询接口可以返回4条数据,那么导出功能以同样的参数也应该导出4条数据的excell。

导出传参:

结果:

导出文件但是打不开,原因可能是:传参的问题;接口返回的数据有问题。 

能够导出,不能打开文件。

 经过排查,是前端请求有问题,如下图:

能够导出,并且能够打开的。get请求的响应体和请求体的结构。如下:请求传参带上,responseType: 'blob',【必须带上】

二、导出文件使用post请求 

前端界面:

导出传参:

总结:

导出功能,不管前端使用的是get或者post请求,都需要后端对接受到的传参进行识别,进而返回对应的响应体。 

前端请求一定要带上responseType: 'blob',


http://www.ppmy.cn/ops/149474.html

相关文章

Java 泛型的用法

1. 泛型类 泛型类是指在类定义时使用类型参数来指定类的类型。这样可以在类的内部使用这些类型参数来定义字段、方法的返回类型和参数类型。 public class Box<T> {private T t;public void set(T t) {this.t t;}public T get() {return t;} }在这个例子中&#xff0c…

25年无人机行业资讯 | 1.1 - 1.5

25年无人机行业资讯 | 1.1 - 1.5 中央党报《经济日报》刊文&#xff1a;低空经济蓄势待发&#xff0c;高质量发展需的平衡三大关系 据新华网消息&#xff0c;2025年1月3日&#xff0c;中央党报《经济日报》发表文章指出&#xff0c;随着国家发展改革委低空经济发展司的成立&a…

使用JMeter模拟多IP发送请求!

你是否曾遇到过这样的场景&#xff1a;使用 JMeter 进行压力测试时&#xff0c;单一 IP 被服务器限流或者屏蔽&#xff1f;这时&#xff0c;如何让 JMeter 模拟多个 IP 发送请求&#xff0c;成功突破测试限制&#xff0c;成为测试工程师必须攻克的难题。今天&#xff0c;我们就…

交响曲-24-3-单细胞CNV分析及聚类

CNV概述 小于1kb是常见的插入、移位、缺失等的变异 人体内包含<10% 的正常CNV&#xff0c;我们的染色体数是两倍体&#xff0c;正常情况下&#xff0c;只有一条染色体表达&#xff0c;另一条沉默&#xff0c;当表达的那条染色体发生CNV之后&#xff0c;表达数量就会成倍增加…

【UE5 C++课程系列笔记】27——多线程基础——ControlFlow插件的基本使用

目录 步骤 一、搭建基本同步框架 二、添加委托 三、添加蓝图互动框架 四、修改为异步框架 完整代码 通过一个游戏初始化流程的示例来介绍“ControlFlows”的基本使用。 步骤 一、搭建基本同步框架 1. 勾选“ControlFlows”插件 2. 新建一个空白C类&#xff0c;这里…

<C++学习>C++ Boost 与 std 的对比

Boost 与 std 的对比 Boost 和 std(C++ 标准库)是 C++ 开发中两个重要的工具集,它们在设计理念、功能范围和使用方式上存在一些差异。以下是详细的比较和分析: 1. 定义与背景 Boost 来源:Boost 是一个开源的 C++ 库集合,由志愿者开发,首次发布于 1998 年。目标:提供高…

JavaScript系列(20)-- Generator应用详解

JavaScript Generator应用详解 &#x1f31f; 今天&#xff0c;让我们深入探讨JavaScript中Generator的应用。Generator是ES6引入的强大特性&#xff0c;它为异步编程和迭代提供了优雅的解决方案。 Generator基础回顾 &#x1f3af; &#x1f4a1; 小知识&#xff1a;Generat…

JavaEE之定时器及自我实现

在生活当中&#xff0c;有很多事情&#xff0c;我们不是立马就去做&#xff0c;而是在规定了时间之后&#xff0c;在到该时间时&#xff0c;再去执行&#xff0c;比如&#xff1a;闹钟、定时关机等等&#xff0c;在程序的世界中&#xff0c;有些代码也不是立刻执行&#xff0c;…