Vue网络请求 文件上传与下载的最佳实践
Vue 网络请求基础
在 Vue 项目中,网络请求是与后端服务器进行数据交互的重要手段。目前,Axios 是 Vue 生态中广泛使用的网络请求库,它具有简洁易用、支持 Promise API、能在浏览器和 Node.js 中使用等优点。
安装 Axios
在 Vue 项目的根目录下,通过 npm 或 yarn 安装 Axios:
npm install axios --save
# 或者
yarn add axios
在 Vue 中使用 Axios
在 main.js 文件中,通常会对 Axios 进行全局配置和挂载:
import Vue from 'vue'
import axios from 'axios'
// 创建一个 Axios 实例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // 基础 URL
timeout: 5000 // 请求超时时间
})
// 请求拦截器
service.interceptors.request.use(
config => {
// 在发送请求之前做些什么,比如添加 token
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config
},
error => {
// 对请求错误做些什么
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
// 对响应数据做些什么
return response.data
},
error => {
// 对响应错误做些什么
if (error.response.status === 401) {
// 处理未授权情况,例如跳转到登录页面
router.push('/login')
}
return Promise.reject(error)
}
)
Vue.prototype.$http = service
这样在 Vue 组件中就可以通过 this.$http
来发起网络请求。例如:
<template>
<div>
<button @click="fetchData">获取数据</button>
<ul>
<li v-for="item in dataList" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
dataList: []
}
},
methods: {
async fetchData() {
try {
const response = await this.$http.get('/api/data')
this.dataList = response
} catch (error) {
console.error('请求失败', error)
}
}
}
}
</script>
文件上传的最佳实践
文件上传是前端开发中常见的需求,在 Vue 中结合 Axios 可以优雅地实现文件上传功能。
使用 FormData 对象
FormData 是 HTML5 新增的一个对象,用于创建表单数据。它可以轻松地将文件附加到请求中,并且支持异步上传。
<template>
<div>
<input type="file" @change="handleFileChange" />
<button @click="uploadFile">上传文件</button>
</div>
</template>
<script>
export default {
data() {
return {
file: null
}
},
methods: {
handleFileChange(event) {
this.file = event.target.files[0]
},
async uploadFile() {
if (!this.file) {
return
}
const formData = new FormData()
formData.append('file', this.file)
try {
const response = await this.$http.post('/api/upload', formData, {
headers: {
'Content-Type':'multipart/form-data'
}
})
console.log('文件上传成功', response)
} catch (error) {
console.error('文件上传失败', error)
}
}
}
}
</script>
在上述代码中,通过 input
元素的 change
事件获取选中的文件,然后将文件添加到 FormData 对象中。在 Axios 的 post
请求中,设置 Content-Type
为 multipart/form-data
来告知服务器这是一个包含文件的表单数据请求。
显示上传进度
对于较大文件的上传,显示上传进度可以提供更好的用户体验。Axios 支持在请求配置中通过 onUploadProgress
回调函数来获取上传进度。
<template>
<div>
<input type="file" @change="handleFileChange" />
<button @click="uploadFile">上传文件</button>
<div v-if="uploadProgress">
上传进度: {{ uploadProgress }}%
</div>
</div>
</template>
<script>
export default {
data() {
return {
file: null,
uploadProgress: null
}
},
methods: {
handleFileChange(event) {
this.file = event.target.files[0]
},
async uploadFile() {
if (!this.file) {
return
}
const formData = new FormData()
formData.append('file', this.file)
try {
const response = await this.$http.post('/api/upload', formData, {
headers: {
'Content-Type':'multipart/form-data'
},
onUploadProgress: progressEvent => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
this.uploadProgress = percentCompleted
}
})
console.log('文件上传成功', response)
this.uploadProgress = null
} catch (error) {
console.error('文件上传失败', error)
this.uploadProgress = null
}
}
}
}
</script>
在这个例子中,onUploadProgress
回调函数接收一个 progressEvent
对象,通过计算 loaded
和 total
的比例来获取上传进度,并实时更新 uploadProgress
数据,从而在模板中显示上传进度。
多文件上传
多文件上传只需稍微修改代码,处理多个文件即可。
<template>
<div>
<input type="file" multiple @change="handleFileChange" />
<button @click="uploadFiles">上传文件</button>
<div v-if="uploadProgress">
上传进度: {{ uploadProgress }}%
</div>
</div>
</template>
<script>
export default {
data() {
return {
files: [],
uploadProgress: null
}
},
methods: {
handleFileChange(event) {
this.files = Array.from(event.target.files)
},
async uploadFiles() {
if (this.files.length === 0) {
return
}
const formData = new FormData()
this.files.forEach(file => {
formData.append('files', file)
})
try {
const response = await this.$http.post('/api/uploadMultiple', formData, {
headers: {
'Content-Type':'multipart/form-data'
},
onUploadProgress: progressEvent => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
this.uploadProgress = percentCompleted
}
})
console.log('文件上传成功', response)
this.uploadProgress = null
} catch (error) {
console.error('文件上传失败', error)
this.uploadProgress = null
}
}
}
}
</script>
这里通过设置 input
的 multiple
属性允许选择多个文件,然后将这些文件逐一添加到 FormData 对象中进行上传。同样利用 onUploadProgress
来显示多文件上传的整体进度。
文件下载的最佳实践
文件下载在前端开发中也经常遇到,Vue 实现文件下载可以通过多种方式,下面介绍几种常见的方法。
使用 a 标签下载
对于已知 URL 的文件,可以直接创建一个 a
标签,并设置其 href
和 download
属性来实现下载。
<template>
<div>
<button @click="downloadFile">下载文件</button>
</div>
</template>
<script>
export default {
methods: {
downloadFile() {
const link = document.createElement('a')
link.href = '/api/download/file.pdf'
link.download = 'file.pdf'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
}
}
</script>
这种方法简单直接,适用于后端返回静态文件链接的情况。但是如果需要通过网络请求获取文件内容再进行下载,就需要其他方法。
通过 Axios 下载二进制文件
当后端返回的是二进制文件流时,可以使用 Axios 来获取文件内容,并通过 Blob
对象和 URL.createObjectURL
来实现下载。
<template>
<div>
<button @click="downloadFile">下载文件</button>
</div>
</template>
<script>
export default {
async downloadFile() {
try {
const response = await this.$http.get('/api/download', {
responseType: 'blob'
})
const blob = new Blob([response], { type: 'application/pdf' })
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = 'file.pdf'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(link.href)
} catch (error) {
console.error('文件下载失败', error)
}
}
}
</script>
在上述代码中,通过设置 Axios 的 responseType
为 blob
来告诉 Axios 期望得到二进制响应。然后将响应内容创建为 Blob
对象,再通过 URL.createObjectURL
创建一个临时 URL 供 a
标签下载使用。下载完成后,需要调用 URL.revokeObjectURL
来释放创建的临时 URL 资源。
处理动态文件名
有时候后端返回的文件名是动态的,这时候可以从响应头中获取文件名。
<template>
<div>
<button @click="downloadFile">下载文件</button>
</div>
</template>
<script>
export default {
async downloadFile() {
try {
const response = await this.$http.get('/api/download', {
responseType: 'blob'
})
const disposition = response.headers['content-disposition']
const matches = /filename=(.*)/.exec(disposition)
let filename = 'default.pdf'
if (matches && matches.length > 1) {
filename = decodeURIComponent(matches[1])
}
const blob = new Blob([response], { type: 'application/pdf' })
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(link.href)
} catch (error) {
console.error('文件下载失败', error)
}
}
}
</script>
这里通过解析 content - disposition
响应头中的文件名信息,动态设置下载文件的名称,确保下载的文件名与后端提供的一致。
下载进度显示
类似于文件上传进度显示,文件下载也可以显示进度。Axios 同样支持通过 onDownloadProgress
回调函数来获取下载进度。
<template>
<div>
<button @click="downloadFile">下载文件</button>
<div v-if="downloadProgress">
下载进度: {{ downloadProgress }}%
</div>
</div>
</template>
<script>
export default {
data() {
return {
downloadProgress: null
}
},
async downloadFile() {
try {
const response = await this.$http.get('/api/download', {
responseType: 'blob',
onDownloadProgress: progressEvent => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
this.downloadProgress = percentCompleted
}
})
const disposition = response.headers['content-disposition']
const matches = /filename=(.*)/.exec(disposition)
let filename = 'default.pdf'
if (matches && matches.length > 1) {
filename = decodeURIComponent(matches[1])
}
const blob = new Blob([response], { type: 'application/pdf' })
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(link.href)
this.downloadProgress = null
} catch (error) {
console.error('文件下载失败', error)
this.downloadProgress = null
}
}
}
</script>
在 Axios 的 get
请求中添加 onDownloadProgress
回调函数,计算下载进度并更新 downloadProgress
数据,从而在模板中显示下载进度。
优化与注意事项
- 错误处理:在文件上传和下载过程中,要全面处理各种可能的错误,如网络错误、服务器错误、文件格式不支持等。在响应拦截器和请求处理的
catch
块中,要根据不同的错误类型给出合适的提示信息,提高用户体验。 - 安全性:在文件上传时,要对上传的文件进行严格的验证,防止恶意文件上传,例如检查文件类型、大小限制等。在下载时,要确保下载链接的来源可靠,避免用户下载到恶意文件。
- 性能优化:对于大文件的上传和下载,可以考虑采用断点续传的方式,减少用户等待时间和网络资源浪费。在上传时,合理设置并发数,避免过多请求导致网络拥塞。
- 兼容性:不同浏览器对于文件操作的支持可能存在差异,要进行充分的兼容性测试,特别是在处理
Blob
对象、URL.createObjectURL
等特性时,确保在主流浏览器中都能正常工作。
通过以上关于 Vue 网络请求中文件上传与下载的最佳实践介绍,希望能帮助开发者在实际项目中更高效、更优雅地实现这些功能,提升用户体验和应用的稳定性。无论是简单的单文件上传下载,还是复杂的多文件操作、进度显示等需求,都可以通过上述方法和技巧来实现。