最常见的方式
我们最熟悉的a标签实现下载功能应该就是直接给a标签的href赋值请求路径,如下所示,这一般适用于后端返回的是一个下载链接的情况
<a download :href="downloadUrl">下载</a>
const downloadUrl = `${location.origin}/admin-api/warn/exportPermissionWarnRule`
But,如果后端返回的是一个文本呢,比如是一个html文本,我们又该如何处理呢?
这个时候有两种处理方案:
第一种:手动创建一个a标签,然后进行赋值等一系列操作,具体实现如下:
const downloadResponse = await AiTestModal.getEvaluationDownloadReport(this.taskId)
const blob = new Blob([downloadResponse], { type: 'text/html' })
const downloadUrl = URL.createObjectURL(blob)
const a = document.createElement('a')
a.style.display = 'none';
a.href = downloadUrl;
a.download = '测试报告.html';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(downloadUrl) //用于释放通过 URL.createObjectURL(blob) 创建的 URL 对象,以释放浏览器资源并减少内存占用
document.body.removeChild(a); //记得最后要销毁
window.URL.revokeObjectURL(downloadUrl) 补充知识(不是重点,内容来自chatgpt)
window.URL.revokeObjectURL(downloadUrl) 用于释放通过 URL.createObjectURL(blob) 创建的 URL 对象,以释放浏览器资源并减少内存占用。这通常用于在不再需要 URL 对象时进行清理。
当您使用 URL.createObjectURL(blob) 创建一个 URL 对象时,浏览器会在内部创建一个映射,将 URL 对象映射到二进制数据(在这种情况下是您的 HTML 内容)。一旦您不再需要这个 URL 对象时,最佳实践是调用 window.URL.revokeObjectURL(downloadUrl) 来释放它,以便浏览器可以正确地清理资源。
通常,在执行下载操作后立即调用 window.URL.revokeObjectURL(downloadUrl) 是一种良好的做法,以确保浏览器能够及时释放资源。这有助于避免内存泄漏和提高性能。
在您的示例中,window.URL.revokeObjectURL(this.downloadUrl) 用于释放生成的 HTML 文件的 URL 资源,以确保浏览器能够正确地进行清理。这是一个良好的做法,尤其是在您的应用中可能频繁地生成和下载大量文件时。
这样写是没有问题的,但是,当我尝试在页面中换种方式,把创建a标签替换成通过类似于getElementById的方式获取页面中已经存在的a标签时,就出现了一些问题......,接下来介绍一下
第二种方式:通过类似于getElementById的方式获取页面中已经存在的a标签来进行一系列操作
以及实现过程中遇到的一些问题:
我最开始的代码是这样写的:
<a id="downloadLink" href="#" @click.prevent="getTestReport">
<el-button type="primary" round plain>
<i class="iconfont icon-daoru"></i> 生成并下载测试报告
</el-button>
</a>
function getTestReport(){
const downloadResponse = await AiTestModal.getEvaluationDownloadReport(this.taskId)
const blob = new Blob([downloadResponse], { type: 'text/html' })
const downloadUrl = URL.createObjectURL(blob)
const downloadLink = document.getElementById('downloadLink')
downloadLink.href = downloadUrl
downloadLink.download = '测试报告.html'
window.URL.revokeObjectURL(downloadUrl)
}
但是我发现downloadUrl拿到了,也成功给a标签的href赋值成功,可是,并没有实现下载。
于是我又想试着在给href赋值之后,重新触发click事件,于是我又加了一句代码:
<a id="downloadLink" href="#" @click.prevent="getTestReport">
<el-button type="primary" round plain>
<i class="iconfont icon-daoru"></i> 生成并下载测试报告
</el-button>
</a>
function getTestReport(){
const downloadResponse = await AiTestModal.getEvaluationDownloadReport(this.taskId)
const blob = new Blob([downloadResponse], { type: 'text/html' })
const downloadUrl = URL.createObjectURL(blob)
const downloadLink = document.getElementById('downloadLink')
downloadLink.href = downloadUrl
downloadLink.download = '测试报告.html'
downloadLink.click() // 新增的代码
window.URL.revokeObjectURL(downloadUrl)
}
于是页面炸了,不停的请求接口(因为我们a标签上面有一个click事件,所以会一直触发)
但是我看了半天,都没有看出来第二种方式的写法有什么问题,还找了同事帮我看,他也没有看出来问题,只是觉得好像哪里写的有点不太对劲,于是我们决定就先用第一种方式来写吧。
但是我不甘心,在下班之余问了其他从事前端工作的朋友,他的一句话点醒了我:“你触发它click后才去赋值的download链接,肯定是不能下载的呀。”
对呀,所以在赋值成功之后,我们还是需要手动再去调用一下click事件,但是如何解决之前会不停的去请求接口的问题呢?
其实很简单,一直触发的原因我们在上面也已经说过了,那我们需要解决的是让他触发一次就好了,于是,我们只用给click加上 .once事件修饰符就好了。
<a id="downloadLink" href="#" @click.prevent.once="getTestReport">
<el-button type="primary" round plain>
<i class="iconfont icon-daoru"></i> 生成并下载测试报告
</el-button>
</a>
function getTestReport(){
const downloadResponse = await AiTestModal.getEvaluationDownloadReport(this.taskId)
const blob = new Blob([downloadResponse], { type: 'text/html' })
const downloadUrl = URL.createObjectURL(blob)
const downloadLink = document.getElementById('downloadLink')
downloadLink.href = downloadUrl
downloadLink.download = '测试报告.html'
downloadLink.click()
window.URL.revokeObjectURL(downloadUrl)
}
这样就成功实现啦!✌️
不过第二种方法在工作中应该也很少用,我们一般都会直接采用直接给a标签的href赋值请求路径和第一种手动创建a标签来实现,第二种就当是学习之初的一些小探索和小发现吧✨。