需求 N: (导出, 下载Excel 表格)
本次是为了导出 (下载)Excel 表格, 但这次是前后端分离的, 需要分别对前后进行处理
解决 A:
后端
... // 省略
// 使用`phpoffice/phpspreadsheet` 开源组件来导出Excel
$filename = 'TeamReward.xlsx';
ob_start();
ob_end_clean();
header('Content-Type: application/vnd.ms-excel'); // .xls 格式
// header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); // .xlsx 格式
header('Content-Disposition: attachment;filename="'.$filename.'"');
header('Cache-Control: max-age=0');
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save('php://output'); // 输出
exit; // 使用`exit`, 并去除`正常的api返回` 方式 例: return json_ok()`
- 使用
phpoffice/phpspreadsheet
开源组件,导出Excel
- 使用
php://output
输出- 使用
exit
, 并去除正常的api返回
方式, 例:return json_ok()
前端 (api 处理方式)
// api (axios)请求, 响应 (处理)
export function export() {
// (* 注意 使用`get/post` 的请求, 参数写法的不同 *)
return axios.post('/account/team/export', {}, { responseType: 'blob' // 1. `post` 请求, 也可以使用`arraybuffer` 类型
// return axios.get('/account/team/export',{ responseType: 'blob', // 2. `get` 请求
}).then(res => {
console.log(res) // 打印结果 (见下面)
const { headers, data } = res
// 创建`Blob` , 用于保存 (输入源)
let blob = new Blob([data], {
type: headers["content-type"] // 使用来自`PHP` 输出时的`headers` 中已经指定的类型
// type: 'application/vnd.ms-excel' // 直接手写指定
})
// 保存`saveAs`, 使用开源组件`file-saver`, (* 注意headers 中`key 的大小写` *)
saveAs(blob, decodeURI(headers['content-disposition'].match(/filename="(.*)"/)[1])) // saveAs(输入源, 输出文件名)
})
}
- 使用
get/post
的两种请求, 参数写法的不同- 可使用
responseType: 'blob'
或responseType: 'arraybuffer'
- 创建
Blob
对象- 使用
file-saver
开源组件的saveAs
,保存
打印返回结果
- 返回结果
data: ArrayBuffer type: "application/vnd.ms-excel"
- 返回结果
data: Blob type: "application/vnd.ms-excel"
响应类型
返回(特殊)结果
data: Blob type: "application/json"
(失败结果, 错误造成
, type 并非输出时指定的application/vnd.ms-excel
)
题外话 E:
- 当时是测试
post/get
两种请求时出现的, 当时是使用注释路由
的, 结果是post/get
修改不生效似的, 出现不明的错误, 结果就产生了上面的特殊结果data: Blob type: "application/json"
, 最后发现原因是ThinkPHP
的APP_DEBUG = false
被关了, 导致runtime/cache
不热更新 (注释路由
不起效似的) (: ) 都是猜的, 不知是否正确)axios
的响应拦截处理// axios 的响应拦截处理 service.interceptors.response.use(response => { const res = response.data // 来自`php://output` if (Object.prototype.toString.call(res) === '[object Blob]') { // 判断对象是否为`[对象 Blob]` if (!res.type.includes('application/json')) { // `application/json` 特殊类型 (可能因为错误造成的) const { headers, data } = response return { headers, data } // 返回数据 (* 注意按需组合返回指定数据 *) } } })
axios
响应拦截- Blob 对象判断
if (Object.prototype.toString.call(res) === '[object Blob]') {
- 特殊结果
data: Blob type: "application/json"
参考:
- 前
- Vue axios使用Blob下载二进制流文件 - 简书 (jianshu.com)
- Vue通过Blob对象实现导出Excel功能
- axios blob 返回json特殊情况处理 - 简书 (jianshu.com)
- 前端js/vue下载后台传过来的流文件(如excel)并设置下载文件名 - SegmentFault 思否
- 后
- PHP php://input、php://output用法解析 - 爱E族 (aiezu.com)
- Thinkphp5中使用PhpSpreadsheet实现excel的导入导出_夜空の雪風的博客-CSDN博客_phpspreadsheet
- PhpOffice/PhpSpreadsheet读取和写入Excel - 简书 (jianshu.com)