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

背景:

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

相关推荐
微臣酒驾来迟几秒前
el-descriptions-item使用span占行不生效
前端·javascript·vue.js
明月看潮生24 分钟前
青少年编程与数学 02-006 前端开发框架VUE 22课题、状态管理
前端·javascript·vue.js·青少年编程·编程与数学
禾小毅25 分钟前
vue 实现打包并同时上传至服务器端
前端·vue.js
Front_Yue26 分钟前
Vue虚拟DOM:如何提高前端开发效率
前端·javascript·vue.js
Super毛毛穗26 分钟前
Vue3学习总结
javascript·vue.js·学习
林涧泣28 分钟前
【Uniapp-Vue3】组合式API中的组件的生命周期函数(钩子函数)
前端·javascript·uni-app
黄团团40 分钟前
Vue2+OpenLayers使用Overlay实现点击获取当前经纬度信息(提供Gitee源码)
vue.js
疯狂的沙粒1 小时前
对受控组件和非受控组件的理解?应用场景?
前端·javascript·react.js·前端框架
十二测试录1 小时前
2024最新版Node.js下载安装保姆级教程【图文详解】
javascript·经验分享·程序人生·npm·node.js·appium
wfsm1 小时前
webpack03
前端·javascript·vue.js