一、a标签download下载
后端返回一个可下载的url文件,或者前端本地保存的文件,通过路径引入下载。
(1)将资源放入前端本地保存,打包后随一起上传自服务器
// 本地资源下载,引入路径即可,这里的路径指的是打包后文件与代码文件的相对路径
<a href="./import-template.xlsx" download target="_blank">下载上传模板
</a>
(2)请求服务器,返回的url放入a标签的href属性中,
下面是伪代码,实现的步骤 先获取url,将其作为变量,放入a标签的href属性中,不同的框架方法不同。
let requestUrl = 'xxxxxx';
let responseUrl = '';
fetch(requestUrl ).then(res => {responseUrl = res.data.url
})// dom节点渲染如果responseUrl出现跨域,download将失效,无法下载
<a href=`${responseUrl }` download target="_blank">下载上传模板
</a>
(3) 不依赖框架,原生js实现点击下载的方式
//点击某个按钮触发事件const onClickDownFile = (id) => {
let requestUrl = `xxxxxx${id}xxxx`;
fetch(requestUrl).then(res => {//为了解决a标签跨域无法下载的问题const x = new XMLHttpRequest();x.open('GET', res.data.url, true);x.responseType = 'blob';x.onload = () => {const dataurl = window.URL.createObjectURL(x.response);const a = document.createElement('a');a.href = res.data.url;a.download = res.data.url;a.click();a.remove();};x.send();
})};
二、后端接口返回二进制流,前端需要接收处理
import { stringify } from 'qs';type Method = 'GET' | 'POST';/*** 下载文件* @param url 接口路径* @param data 请求的参数* @param method 请求方式* @param type 下载文件的类型* @param fileName 自定义文件名称*/
export const download = (url: string,data: any,method: Method,type: string,fileName?: string,
) => {/** fetch 配置项 */const params: RequestInit = {method,headers: {Authorization: '','response-type': 'arraybuffer',},};if (method === 'GET') {// 每次请求添加时间戳,避免 GET 请求遭遇 HTTP 缓存data._ = new Date().getTime();// 请求参数合并到 URL 上url += `?${stringify(data)}`;} else {params.body = JSON.stringify(data);}return fetch(url, params).then((response) => {if (response.status === 200 && response.body) {if (!fileName) {const cd = response.headers.get('content-disposition');const cds = cd?.split('filename=') || [];if (cds[1]) {fileName = decodeURIComponent(cds[1]);}}return response.blob();}return Promise.reject();}).then((_blob) => {const blob = new Blob([_blob], {type,});const link = document.createElement('a');link.href = window.URL.createObjectURL(blob);link.download = fileName || 'file';link.click();return 'done';});
};
使用方式
download('xxxx',{},"GET", //或者"POST"'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8',fileName, //文件名称)
type是文件类型,不同的文件类型type不同
后缀 | MIME Type |
---|---|
.doc | application/msword |
.docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
.xls | application/vnd.ms-excel |
.xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
.ppt | application/vnd.ms-powerpoint |
.pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation |
三、后端视情况返回二进制流文件。
我在一个项目中遇到这样一个需求,前端导入文件,后端解析并返回结果,如果成功,返回的data为null,code为0,如果失败,返回一个excel的二进制流文件,在excel文件中显示的是失败的原因,并自动下载文件。
const downloadBlob = (blob: Blob, type: string, fileName?: string) => {const newBlob = new Blob([blob], {type,});const link = document.createElement('a');link.href = window.URL.createObjectURL(newBlob);link.download = fileName || '错误文件';link.click();
};export const FileUpload = (url: string, file: FormData): Promise => {/** fetch 配置项 */const params: RequestInit = {method: 'POST',headers: {Authorization: '',},body: file,};return fetch(url, params).then((response) => {if (response.status === 200 && response.body) {return new Promise((resolve, reject) => {const contentType = response.headers.get('content-type') || '';if (contentType.indexOf('application/json') >= 0) {// 返回的是 JSON 文件response.json().then(resolve);} else if (contentType.indexOf('application/vnd.ms-excel') >= 0) {// 返回的是 excel 文件response.blob().then((_blob) => {const cd = response.headers.get('content-disposition');const cds = cd?.split('filename=') || [];downloadBlob(_blob,'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8',decodeURIComponent(cds[1]),);resolve();});} else {reject();}});}return Promise.reject();});
};
content-type 的类型有很多种,每一种对应不同的返回类型,
content-type对照大全 : https://tool.oschina.net/commons/