http 头部的 Content-Disposition字段,规定了返回的内容用什么形式展现html
value | 含义 | 是否默认 |
---|---|---|
inline | 以网页或者页面的一部分 | 是 |
attachment | 以附件的形式下载并保存到本地 | 否 |
http.createServer((req, res) => {
res.setHeader("Content-Disposition", "attachment")
res.end('123 - 321 - 1234567')
})
复制代码
前端须要使用window.open 形式访问 此路由就能够实现文件的下载前端
window.open(xxxx)
复制代码
或者使用 H5新属性 a 标签mysql
<a type='download' href=xxx> 点击下载 </a>
复制代码
http.createServer((req, res) => {
res.setHeader("Content-Type", "application/octet-stream")
res.end('123 - 321 - 1234567')
})
复制代码
同上前端使用 open 或者 a标签进行处理ios
备注: 若是使用普通的请求,是不能够的,好比使用ajaxajax
window.open 能够下载文件的缘由是,浏览器遇到没法解析的文件就是执行下载sql
当使用浏览器打开文件时, 若是它没法解析,那么就会把该文件下载下来axios
http.createServer((req, res) => {
const data = fs.readFileSync('./Zip.zip')
res.end(data)
})
复制代码
拿到的data是一个buffer 对象,那么直接写一个Buffer能够实现下载么数组
data = new Buffer('我是谁,谁是我')
复制代码
尝试后发现,koa是能够的。可是原生直接这么写是不行的浏览器
http.createServer((req, res) => {
const newBuf = new Buffer('我是谁,谁是我')
res.end(newBuf)
})
复制代码
在实际项目中,文件下载可能出现的场景服务器
对于已经在服务器存在的文件进行下载,好比图片资源
将一些查询数据导出到本地, 好比mysql查询结果导出csv
对于已存在的资源,能够直接使用 上面说的 winodw.open 或者 a 标签进行下载,那么对于不是以文件形式存在的资源呢
常常见到使用 data URI scheme 的是图片, 通常为了减小http请求,会将图片直接以base64的形式展现在html中
<img src='data:image/png;base64,xxxxxx'/>
复制代码
也能够应用到文件下载中
接口
router.get('/download', async (ctx, next) => {
const newBUf = new Buffer('我是谁,谁是我')
ctx.body = newBUf
}
复制代码
前端
axios.get(`${path}`)
.then(function ({data}) {
let a = document.createElement('a');
a.href = "data:text/plain;charset=utf-8," + data;
a.download = "myfilename.png";
a.click();
})
复制代码
Blob 是表示一个类文件对象,能够用它来表示一个文件
server 部分
http.createServer((req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
let end = ''
if (req.url.includes("/down")) {
const newBUf = new Buffer('我是谁,谁是我')
end = newBUf
}
res.end(end)
}
复制代码
前端
const xhr = new XMLHttpRequest();
xhr.open('GET', path);
xhr.responseType = 'blob';
xhr.onload = function () {
const blob = xhr.response;
const url = URL.createObjectURL(blob);
// 经过a标签去下载
const link = document.createElement('a');
link.href = url;
link.download = fileName;
link.click();
URL.revokeObjectURL(url);
};
xhr.send();
复制代码
尝试用 window.open 方式,发现不能够
目前经常使用的各个请求库也支持返回内容为blob格式
axios.get(`${path}`, {
responseType: 'blob'
})
复制代码
或者 fetch
fetch(path).then(res => res.blob().then(blob =>{ ... })
复制代码
createObjectURL
用 blob 对象来建立一个 object URL(它是一个 DOMString),咱们能够用这个 object URL 来表示某个 blob 对象,这个 object URL 能够用在 a 标签的 href属性上,而后触发点击事件,就能够下载文件了
revokeObjectURL
为了不避免内存泄漏,须要手动释放建立的 object URL
从代码就能看出,须要先将返回内容转为blob才进行下载操做,若是用户操做的是一个很大的资源,在等待文件正式下载前,还须要一段时间等待格式的转化
这边直接用数据模拟mysql查询结果, 在网上查到两种拼接方式,确定有其他的方案
const result = [
['id', 'oreder', 'name', 'status' ],
[1, '201904120201', '正在加载', '已完成'],
[18, '201904120204', '测试189', '待付款'],
[22, '201904120209', '蓝田日暖', '待付款'],
]
// 能够看到 result 数组的第一组是 csv的各个字段标题
const data = csvData.reduce((cur, next) => `${cur + next.join(',')}\n`, '')
const blob = new Blob([data]);
const url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
复制代码
const result = [
[1, '201904120201', '正在加载', '已完成'],
[18, '201904120204', '测试189', '待付款'],
[22, '201904120209', '蓝田日暖', '待付款'],
]
// result 就是正常的数据格式
// 解决乱码问题
let dataType = '\uFEFF'
// 添加表格的头子段
dataType += ([' 订单编号', '用户', '动态ID'].join(','))
dataType += '\n'
result.forEach(item => {
// dataType += ([item.id, item.order, item.name, item.staus].join(','))
dataType += (item.join(','))
dataType += '\n'
})
const blob = new Blob([dataType], {type: 'text/csv'})
复制代码
确定是在 server端进行文件的拼接操做,而后返回到前端下载
大体过程
const content1 = '这里是第1段文件内容'
const content2 = '这里是第2段文件内容'
ctx.body = { code:200, data: content1 + content2 }
复制代码
前端就是上文中的blob 下载方式了
如图,使用wps打开会被展现xxxE+15
开发时候使用Mac自带的numbers是没有问题的,没有发现换一个软件就不行了,处理方案从网络上查到的,给过长的数据添加一个 /t 就能够了 [使用 ' 半角单引号 或者 \t 均可以]
result.id = `${id}\t`
复制代码
其实这个问题是在查询资料的过程有人提到的问题
批量写了多个 window.open 在Chrome中 能够正常使用,可是在safari中,只能打开一个窗口,下载一个文件
感受若是将全部文件合并成为一个xxx.zip 而后对 这个zip文件作下载。不过这种方式有问题,目前查到的大部分过程都是会在服务器新建出一个 zip 文件,等下载完毕在作删除,尚未找到能够跨过这一步的方式。