如何通过ajax实现文件流下载

10,316 阅读2分钟

前言

每次遇到文件导出的时候,总是先通过参数拼接得到一个url,然后通过创建a标签window.open(url, '_blank')的方式打开链接实现下载,这种确实是最简单最方便的方式,but有一天,为了不让所有知道导出链接的人都能导出文件(老板总说公司的内部统计数据无价 不能泄露 额~~~),所以需要有一种鉴权的方式。

此时有一位后端的靓仔自信满满滴说:“把token设置到headers里面,再通过a标签打开不就可以了吗?”
前端切图仔一脸懵逼:“通过打开链接的方式还能设置headers? 键盘给你 你来!!”

image.png 争论一波之后,最终两人决定把token作为参数放在url里面,后端直接拿url的token进行鉴权。 但是有一天,技术部的老大看到这对“卧龙凤雏”把token暴露在url上的“壮举”后,狠狠的批了这对孙子的安全性意识,并下令修改。

所以,通过ajax实现文件下载鉴权是比较安全稳妥的方式

image.png

1.原生ajax下载

注意,我在这里直接导出文件,没有进行错误判断,参数也没有拼接,请自行处理。

download1() {
	const url = '导出链接'; //记得拼接参数
	const xhr = new XMLHttpRequest();
	xhr.open('GET', url, true); // 也可以使用POST方式,根据接口
	xhr.setRequestHeader('token', sessionStorage.getItem('token')); // 设置token
	xhr.setRequestHeader('Content-Type', 'application/octet-stream');
	xhr.responseType = 'blob'; // 返回类型blob
	xhr.onload = function(e) {
		if (this.status === 200) {
			const blob = this.response;
			const reader = new FileReader();
			reader.readAsDataURL(blob); // 转换为base64,可以直接放入a表情href
			reader.onload = function(e) {
				const a = document.createElement('a');
				a.download = '文件名.xls';
				a.href = e.target.result;
				document.documentElement.appendChild(a);
				a.click();
				a.remove(); // 等价于document.documentElement.removeChild(a);
			};
		}
	};
	xhr.send(); // 发送ajax请求
}

2.axios请求下载

download2() {
        const url = '导出链接';
	$get(url , {
		startDate: '2021-08-01',
		endDate: '2021-08-01'
	}).then(res => {
		res.request.onload = function(e) {
			const blob = res.data;
			const reader = new FileReader();
			reader.readAsDataURL(blob);
			reader.onload = function(e) {
				const a = document.createElement('a');
				a.download = '文件名.xls';
				a.href = e.target.result;
				document.documentElement.appendChild(a);
				a.click();
				a.remove();
			};
		};
	});
}
// axios代码
const req = axios.create({
	responseType: 'blob' //关键
});
req.interceptors.request.use(
	config => {
		config.headers = {
			token: sessionStorage.getItem('token'),
			'Content-Type': 'application/octet-stream'
		};
		return config;
	},
	err => {
		return Promise.reject(err);
	}
);
req.interceptors.response.use(
	res => {
		// 这里做错误判断(这里假设有token直接返回文件流 没有token返回的res包含code)
		if (res.hasOwnProperty('code') && res.code !== 0) {
			alert(res.message || '导出错误');
			return;
		}

		return res;
	},
	err => {
		console.log(err, err.message);
	}
);

function $get(url, params = {}) {
	return new Promise((resolve, reject) => {
		req.get(url, {
			params
		}).then(
			res => {
				resolve(res);
			},
			err => {
				reject(err);
			}
		);
	});
}

完整代码

<template>
	<div class="page">
		<el-button @click="download1">原生方法下载</el-button>
		<el-button @click="download2">使用axios下载</el-button>
	</div>
</template>

<script>
import axios from 'axios';
const req = axios.create({
	responseType: 'blob' //关键
});
req.interceptors.request.use(
	config => {
		config.headers = {
			token: sessionStorage.getItem('token'),
			'Content-Type': 'application/octet-stream'
		};
		return config;
	},
	err => {
		return Promise.reject(err);
	}
);
req.interceptors.response.use(
	res => {
		// 这里做错误判断(这里假设有token直接返回文件流 没有token返回的res包含code)
		if (res.hasOwnProperty('code') && res.code !== 0) {
			alert(res.message || '导出错误');
			return;
		}

		return res;
	},
	err => {
		console.log(err, err.message);
	}
);
function $get(url, params = {}) {
	return new Promise((resolve, reject) => {
		req.get(url, {
			params
		}).then(
			res => {
				resolve(res);
			},
			err => {
				reject(err);
			}
		);
	});
}
export default {
	data() {
		return {};
	},
	methods: {
		download1() {
			const url = '导出链接'; //记得拼接参数
			const xhr = new XMLHttpRequest();
			xhr.open('GET', url, true); // 也可以使用POST方式
			xhr.setRequestHeader('token', sessionStorage.getItem('token')); // 设置token
			xhr.setRequestHeader('Content-Type', 'application/octet-stream');
			xhr.responseType = 'blob'; // 返回类型blob
			xhr.onload = function(e) {
				if (this.status === 200) {
					const blob = this.response;
					const reader = new FileReader();
					reader.readAsDataURL(blob); // 转换为base64,可以直接放入a表情href
					reader.onload = function(e) {
						const a = document.createElement('a');
						a.download = '文件名.xls';
						a.href = e.target.result;
						document.documentElement.appendChild(a);
						a.click();
						a.remove(); // 等价于document.documentElement.removeChild(a);
					};
				}
			};
			xhr.send(); // 发送ajax请求
		},
		download2() {
			const url = '导出链接';
			$get(url, {
				startDate: '2021-08-01',
				endDate: '2021-08-01'
			}).then(res => {
				res.request.onload = function(e) {
					const blob = res.data;
					const reader = new FileReader();
					reader.readAsDataURL(blob);
					reader.onload = function(e) {
						const a = document.createElement('a');
						a.download = '文件名.xls';
						a.href = e.target.result;
						document.documentElement.appendChild(a);
						a.click();
						a.remove();
					};
				};
			});
		}
	}
};
</script>

<style lang="less"></style>

补充

上面的代码亲测能够正常实现文件流的导出,成功实现了鉴权导出。 代码里面参数、token、错误判断这些需要根据自己的业务需求进行修改。