这段时间项目需要下载文件,刚开始直接用a标签的href加后端地址的方式就可以下载,这类方法可以下载zip文件等浏览器不能识别的文件,但是遇到下载txt和图片等文件就莫得办法了,因为浏览器会自动打开。

然后就去网上搜了一下,发现有很多人都说直接在a标签里加一个download属性就好了,并且这个可以设置文件名,但是很多都没有接受什么时候加这个属性会生效,所以用我亲自踩完坑的经历来解释一下,先看一个列子,比如:

点击下载

各位可以运行上面这段代码试一试,地址是有效的

但是,很灵性的但是哈。(各位,重点来了,一定要仔细看)

但是这种方法根本不管用!浏览器还是直接打开图片了,原因是a标签加这个属性得分情况,必须要在同源(同域名、同协议、同端口号)时,才会生效,什么概念呢?

比如说前端项目布在http://192.168.88:8888 这个服务器上面,后端项目也布在同一个服务器同一个端口号下面,这种情况才可以下载,否则的话都不行。

并且就算是以上这种情况前端在本地调试的时候也是不会下载的,原因是在本地调试的时候前端地址一般都是http://localhost:300,很明显和后端地址不是同源嘛。

虽然做了反向代理,但还是不会下载的,不过推到线上就生效了。所以各位在用a标签下载的时候需要注意是否同源(主要看发布后是否同源)

既然说了ajax的方法肯定就还没完嘛,接下来讲一下第二种

先上代码:

//ajax方法

const xhr = new XMLHttpRequest();

xhr.open('GET', 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', true);

xhr.responseType = 'blob';

xhr.onload = function () {

if (this.status === 200) {

const fileName = 'test.png';

const blob = new Blob([this.response]);

const blobUrl = window.URL.createObjectURL(blob);

const a = document.createElement('a');

a.href = blobUrl;

a.download = fileName;

a.click();

window.URL.revokeObjectURL(blobUrl);

}

};

xhr.send();

// 用fetch发送请求

fetch('https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png').then((res) => {

res.blob().then((blob) => {

const blobUrl = window.URL.createObjectURL(blob);

console.log(blob);

console.log(blobUrl);

// 这里的文件名根据实际情况从响应头或者url里获取

const filename = 'user.jpg';

const a = document.createElement('a');

a.href = blobUrl;

a.download = filename;;

a.click();

window.URL.revokeObjectURL(blobUrl);

});

});

// axios下载

import axios from "axios";

import { message } from 'antd'

export const downloadFile = (method, url, config) => {

let _data = null

let _params = null

if (method === 'GET' || method === 'get') {

_params = config.params

} else {

_data = config.data

}

axios({

url,

method,

params: _params,

data: _data,

responseType: 'blob',

headers: {

'Csrf-Token': '123',

},

}).then(res => {

console.log(res)

const str = res.headers['content-disposition']

if (!res || !str) {

message.error(res.message || '下载失败!')

return

}

if (res && res.status === 200 && res.data) {

const { data, headers, config } = res

let fileName

if (headers['content-disposition']) {

fileName = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1')

} else if (data.fileName) {

fileName = data.fileName

} else {

fileName = config.params?.fileName

}

// 此处当返回json文件时需要先对data进行JSON.stringify处理,其他类型文件不用做处理

const blob = new Blob([data], { type: headers['content-type'] })

const dom = document.createElement('a')

const downUrl = window.URL.createObjectURL(blob)

dom.href = downUrl

dom.download = decodeURIComponent(fileName)

dom.style.display = 'none'

document.body.appendChild(dom)

dom.click()

dom.parentNode.removeChild(dom)

window.URL.revokeObjectURL(url)

} else {

message.error(res.message || '下载失败!')

}

}).catch(err => {

message.error(err || '下载失败!')

})

}

上面的代码也是可以运行的,地址有效

买一送一再来了一个fetch的

这种方法就不管同源不同源都会下载了,但是这毕竟是ajax请求了嘛,所以会涉及到跨域的问题,需要解决一下跨域问题,不过现在都会配置反选代理,所以也不是上面问题了,但是会有些小细节(如果不细节效果也出不来)

下面看实际项目中的用法

正常来说在项目中请求数据时地址都会写成/aip/xxx/xxx的对吧,所以这个地方一般会写成fetch("/api/xxx/xxx.txt"),对吧,看似很合理,但这样写了点击下载没有反应,也不报错,network也请求成功,但就是不会下载,为什么呢?我也不知道,哪个大佬要是知道一定评论解释一下。

但是我有个解决方案,只要把api换成后端地址就ok了,写成这样:fetch("/http://192.168.88:8888/xxx/xxx.txt"),前面那个"/"必须留着,否则就包跨域的错了,或者也可以写成fetch("http:localhost:3000/http://192.168.88:8888/xxx/xxx.txt")也可以,但是这种前面会被写死,等上线的时候还得改成线上的地址,所以推荐第一种,第一种会自动补全前面的地址,请求完的结果是:

 接下来看项目实际代码:

 

下载结果:

 这一看就知道是react加antd的项目了

好了,具体两种用法就是这样了,都是自己亲测有效的,大家根据自己情况选择。其实还有其他的方式,一是我懒了没去研究,二是我觉得有这两种就够用了

相关阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: