前言

在 Vue中实现请求并下载导入模板的功能,通常需要后端提供一个模板文件的下载接口。 下列以Vue3为例。

1.基本实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { ref } from 'vue'
import axios from 'axios' // 或使用你项目中的请求库
import { ElMessage } from 'element-plus'

// 方法一:直接使用 fetch 或 axios 下载
const downloadTemplate = async () => {
try {
// 替换为你的实际API地址
const response = await axios.get('/api/template/download', {
responseType: 'blob', // 重要:指定响应类型为 blob
params: {
templateType: 'your-template-type' // 如果有不同类型的模板
}
})

// 创建下载链接
const url = window.URL.createObjectURL(new Blob([response.data]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', '导入模板.xlsx') // 设置文件名
document.body.appendChild(link)
link.click()

// 清理
document.body.removeChild(link)
window.URL.revokeObjectURL(url)

ElMessage.success('模板下载成功')
} catch (error) {
ElMessage.error('下载失败: ' + (error.message || '请联系管理员'))
}
}

2.封装成可复用组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<template>
<el-button
type="primary"
:loading="downloading"
@click="handleDownload"
>
下载导入模板
</el-button>
</template>

<script setup>
import { ref } from 'vue'
import axios from 'axios'
import { ElMessage } from 'element-plus'

const downloading = ref(false)

const handleDownload = async () => {
downloading.value = true
try {
const response = await axios.get('/api/template/download', {
responseType: 'blob',
params: { type: 'import' }
})

// 从响应头获取文件名
const contentDisposition = response.headers['content-disposition']
let fileName = '导入模板.xlsx'
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(/filename=(.+)/)
if (fileNameMatch && fileNameMatch[1]) {
fileName = decodeURIComponent(fileNameMatch[1])
}
}

const url = window.URL.createObjectURL(new Blob([response.data]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)

ElMessage.success('模板下载成功')
} catch (error) {
ElMessage.error(`下载失败: ${error.message || '请联系管理员'}`)
} finally {
downloading.value = false
}
}
</script>

3. 使用文件流下载(适用于大文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const downloadLargeTemplate = async () => {
try {
const response = await fetch('/api/template/download-large')
const reader = response.body.getReader()
const contentLength = +response.headers.get('Content-Length')
let receivedLength = 0
let chunks = []

while(true) {
const { done, value } = await reader.read()
if (done) break

chunks.push(value)
receivedLength += value.length
console.log(`下载进度: ${Math.round(receivedLength / contentLength * 100)}%`)
}

const blob = new Blob(chunks)
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = '大型导入模板.xlsx'
a.click()
URL.revokeObjectURL(url)
} catch (error) {
ElMessage.error('下载失败: ' + error.message)
}
}

4.后端配合示例(Node.js Express)

1
2
3
4
5
6
7
8
9
10
11
// 后端路由示例
router.get('/api/template/download', (req, res) => {
const filePath = path.join(__dirname, '../templates/import-template.xlsx')
const fileName = '导入模板.xlsx'

res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
res.setHeader('Content-Disposition', `attachment; filename=${encodeURIComponent(fileName)}`)

const fileStream = fs.createReadStream(filePath)
fileStream.pipe(res)
})

5.使用第三方库(如 file-saver)

1
npm install file-saver

然后使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { saveAs } from 'file-saver'

const downloadWithFileSaver = async () => {
try {
const response = await axios.get('/api/template/download', {
responseType: 'blob'
})
saveAs(response.data, '导入模板.xlsx')
ElMessage.success('模板下载成功')
} catch (error) {
ElMessage.error('下载失败: ' + error.message)
}
}

注意事项

  1. 确保后端设置了正确的 Content-TypeContent-Disposition 响应头
  2. 对于大文件,考虑使用流式下载并显示进度条
  3. 下载完成后要及时释放创建的 URL 对象,避免内存泄漏
  4. 根据实际业务需求调整文件名和错误处理逻辑