背景
对于文件下载,一般简单的文件直接采用 window.open("url")形式,浏览器新开一个tab页面,能够快速的下载,但对于稍微大一点的文件就不太友好了,页面一片空白什么提示都没有,这时候就需要Blob这种形式来提供提示。
为什么使用 blob
- 二进制数据处理:blob 是一种二进制数据对象,适合处理文件数据。
- 避免内存问题:对于大文件,blob 可以避免将整个文件加载到内存中,减少内存占用。
部分代码
@GetMapping("{projectId}/archiveFile/export-full")
public void exportFull(HttpServletResponse response, @PathVariable String projectId) {
bidProjectArchiveFileExportEOService.exportArchiveProjectFiles(response, projectId);
}
// 注意设置 header 中文乱码
// 设置Zip响应头
public static void setResponseHeaderOfZip(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
response.reset();
response.setHeader("fileName", encodeFileName(fileName));
response.setHeader("fileType", "zip");
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("GB2312"), "ISO8859-1"));
response.setContentType("application/octet-stream; charset=utf-8");
response.setCharacterEncoding("UTF-8");
}
private static String encodeFileName(String fileName) {
return URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\+", "%20");
}
前端
- 使用 Axios 发起 GET 请求时,设置 responseType 为 blob。
- 使用 ElLoading 组件显示加载提示,并使用 setInterval 实现计时功能。
- 从响应头中获取文件名,并使用 decodeURIComponent 解码文件名。
- 使用 ElMessage 组件进行消息提示。 通过以上步骤,你可以在文件下载过程中显示带有计时的加载提示,并在文件下载完成后显示相应的成功或失败消息
import axios from 'axios'
import { ElMessage, ElLoading } from 'element-plus'
/**
* Blob 形式下载文件
*
* @param url 目标地址
*/
export function downloadFileByBlob(url) {
let loading
let startTime = new Date().getTime()
let intervalId
loading = ElLoading.service({
lock: true,
text: '文件正在导出中,请稍候... (0s)',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
intervalId = setInterval(() => {
const currentTime = new Date().getTime()
const elapsedTime = Math.floor((currentTime - startTime) / 1000)
loading.setText(`文件正在导出中,请稍候... (${elapsedTime}s)`)
}, 1000)
axios.defaults.timeout = 50000
axios({
url: url,
method: 'GET',
responseType: 'blob', // 重要:设置响应类型为 blob
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
clearInterval(intervalId)
loading.close()
// console.log('downloadFileByBlob', response)
const fileName = decodeURIComponent(response.headers['filename']) || `${new Date().getTime()}.${response.headers['filetype']}`
// 解码文件名
const decodedFileName = decodeURIComponent(fileName)
// 创建一个 URL 对象
const url = window.URL.createObjectURL(new Blob([response.data]))
const a = document.createElement('a')
a.href = url
a.download = decodedFileName // 设置下载文件的名称
document.body.appendChild(a)
a.click()
a.remove()
window.URL.revokeObjectURL(url)
ElMessage.success('文件下载完成')
})
.catch(error => {
clearInterval(intervalId)
loading.close()
console.error('下载文件时出错:', error)
ElMessage.error('文件导出失败')
})
}
使用 blob 形式的优点
- 处理大文件: blob 可以处理大文件,因为它不会将整个文件加载到内存中,而是将其作为二进制数据流处理。
- 兼容性: blob 是现代浏览器广泛支持的,适用于大多数现代浏览器。
- 灵活性: 可以方便地处理不同类型的文件(如 PDF、ZIP、图片等),而不需要额外的处理。
- 安全性: 使用 blob 可以避免直接在 URL 中暴露文件路径,提高安全性 。
使用 blob 形式的缺点
- 性能问题: 对于非常大的文件,创建 blob 对象可能会消耗较多的内存和处理时间。
- 浏览器兼容性: 虽然现代浏览器普遍支持 blob,但一些旧版本的浏览器可能不支持。
- 复杂性:
- 需要更多的代码来处理 blob 对象的创建和下载,增加了代码的复杂性。
- 文件名处理: 需要正确处理文件名的编码和解码,确保文件名在传输过程中不出现乱码
- 移动端的支持问题,兼容性非常差