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

背景:

在项目开发中,对于列表的查询,而后会有导出功能,这里导出的是一个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 = true
        const 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.data
    if (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.data
      const 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',

相关推荐
Sheldon一蓑烟雨任平生3 小时前
Vue3 插件(可选独立模块复用)
vue.js·vue3·插件·vue3 插件·可选独立模块·插件使用方式·插件中的依赖注入
鱼与宇5 小时前
苍穹外卖-VUE
前端·javascript·vue.js
用户47949283569155 小时前
Safari 中文输入法的诡异 Bug:为什么输入 @ 会变成 @@? ## 开头 做 @ 提及功能的时候,测试同学用 Safari 测出了个奇怪的问题
前端·javascript·浏览器
裴嘉靖5 小时前
Vue 生成 PDF 完整教程
前端·vue.js·pdf
毕设小屋vx ylw2824265 小时前
Java开发、Java Web应用、前端技术及Vue项目
java·前端·vue.js
冴羽6 小时前
今日苹果 App Store 前端源码泄露,赶紧 fork 一份看看
前端·javascript·typescript
蒜香拿铁6 小时前
Angular【router路由】
前端·javascript·angular.js
时间的情敌6 小时前
Vite 大型项目优化方案
vue.js
西洼工作室7 小时前
高效管理搜索历史:Vue持久化实践
前端·javascript·vue.js
樱花开了几轉7 小时前
element ui下拉框踩坑
开发语言·javascript·ui