前端解决下载文件,被浏览器打开预览的问题
- 问题
- 解决思路
- 实现
问题
- 后端返回的是文件路径,而不是文件流。
- 需求要下载文件而不是浏览器预览文件。
- 追加需求可以批量下载文件。
解决思路
一般下载文件都是用的window.open() ,但是如果下载的文件浏览器如果支持预览,会被直接打开而不会下载。我们需要用fetch()转成文件流,然后下载。
写一个公共方法,需要调用的地方引用。
实现
// 调用时候 传入需要下载的文件地址的数组就可以
class download {
constructor(urls) {
//所有的url以及文件名
this.urls = urls
//全部队列
this.allQueue = []
//成功文件
this.successFiles = []
//失败文件
this.errorFiles = []
}
//下载文件并转化为blob
fetchFileAsBlob(url, filename) {
if (!url) {
this.errorFiles.push(filename)
return
}
return new Promise((resolve) => {
fetch(url, {
mode: 'cors',
})
.then((res) => {
if (res.status == 200) {
// res返回的是字节流
res
.blob()
.then((blob) => {
// 使用blob()方法返回一个被解析为Blob格式的Promise对象
const blobUrl = window.URL.createObjectURL(blob) // 使用URL.createObjectURL 生成一个blob协议的URL,大多数场景下可以将其当做http协议的url来使用
this.successFiles.push({
url: blobUrl,
name: decodeURIComponent(filename),
})
resolve()
})
.catch(() => {
resolve()
this.errorFiles.push(filename)
})
} else {
resolve()
this.errorFiles.push(filename)
}
})
.catch(() => {
resolve()
this.errorFiles.push(filename)
})
})
}
//统一将blob文件下载并返回错误文件
downloadAllFils(cb) {
Promise.all(this.allQueue).then(() => {
cb(this.errorFiles)
this.successFiles.forEach((item) => {
const a = document.createElement('a') // 创建一个a标签
a.style.display = 'none'
a.href = item.url
a.download = decodeURIComponent(item.name) // 对编码后的 URI 进行解码
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
window.URL.revokeObjectURL(item.url) // 当不结束使用某个 URL 对象之后,应该通过调用这个方法来让浏览器知道不用在内存中继续保留对这个文件的引用了
})
})
}
download(cb) {
this.urls.forEach((item) => {
this.allQueue.push(this.fetchFileAsBlob(item.url, item.filename))
})
this.downloadAllFils((errors) => {
cb(errors)
})
}
}
export { download }
前端解决下载文件,被浏览器打开预览的问题
- 问题
- 解决思路
- 实现
问题
- 后端返回的是文件路径,而不是文件流。
- 需求要下载文件而不是浏览器预览文件。
- 追加需求可以批量下载文件。
解决思路
一般下载文件都是用的window.open() ,但是如果下载的文件浏览器如果支持预览,会被直接打开而不会下载。我们需要用fetch()转成文件流,然后下载。
写一个公共方法,需要调用的地方引用。
实现
// 调用时候 传入需要下载的文件地址的数组就可以
class download {
constructor(urls) {
//所有的url以及文件名
this.urls = urls
//全部队列
this.allQueue = []
//成功文件
this.successFiles = []
//失败文件
this.errorFiles = []
}
//下载文件并转化为blob
fetchFileAsBlob(url, filename) {
if (!url) {
this.errorFiles.push(filename)
return
}
return new Promise((resolve) => {
fetch(url, {
mode: 'cors',
})
.then((res) => {
if (res.status == 200) {
// res返回的是字节流
res
.blob()
.then((blob) => {
// 使用blob()方法返回一个被解析为Blob格式的Promise对象
const blobUrl = window.URL.createObjectURL(blob) // 使用URL.createObjectURL 生成一个blob协议的URL,大多数场景下可以将其当做http协议的url来使用
this.successFiles.push({
url: blobUrl,
name: decodeURIComponent(filename),
})
resolve()
})
.catch(() => {
resolve()
this.errorFiles.push(filename)
})
} else {
resolve()
this.errorFiles.push(filename)
}
})
.catch(() => {
resolve()
this.errorFiles.push(filename)
})
})
}
//统一将blob文件下载并返回错误文件
downloadAllFils(cb) {
Promise.all(this.allQueue).then(() => {
cb(this.errorFiles)
this.successFiles.forEach((item) => {
const a = document.createElement('a') // 创建一个a标签
a.style.display = 'none'
a.href = item.url
a.download = decodeURIComponent(item.name) // 对编码后的 URI 进行解码
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
window.URL.revokeObjectURL(item.url) // 当不结束使用某个 URL 对象之后,应该通过调用这个方法来让浏览器知道不用在内存中继续保留对这个文件的引用了
})
})
}
download(cb) {
this.urls.forEach((item) => {
this.allQueue.push(this.fetchFileAsBlob(item.url, item.filename))
})
this.downloadAllFils((errors) => {
cb(errors)
})
}
}
export { download }