文件下载
1.环境
前端 | Vue3 |
| PrimeVUE |
后端 | Python |
| FastAPI |
2.背景
在一般情况下,文件下载是不需要封装的,尤其是在写代码入门阶段,将文件存储在磁盘上,然后,直接写个 url 就可以下载文件的。
但是,由于现在 微服务 的使用,文件数据的共享,所以需要对文件下载的前后端都进行封装,后端从 MongoDB 中读取文件并返回,前端需要携带令牌来下载文件。
3.后端文件下载源代码
# 下载文件
@router.get(path='/download_file/{oid}')
async def download_file(oid: str=Path(...), current_usr: dict = Depends(find_current_usr)):
fileContent: bytes = None
with MongoClient(settings.MONGO_HOST, settings.MONGO_PORT) as connect:
filedb = connect.filedb
mgfs = GridFS(filedb)
out = mgfs.get(ObjectId(oid))
logger.info('Download file {0} by {1}'.format(out.filename, current_usr['name']))
fileContent = out.read()
async def read_data():
yield fileContent
return Streaming Response(content=read_data())
# 图片Base64字符串
@router.get(path='/download_base64/{oid}')
def base64image(oid: str=Path(...), current_usr: dict = Depends(find_current_usr)):
res = {'base64': ''}
with MongoClient(settings.MONGO_HOST, settings.MONGO_PORT) as connect:
filedb = connect.filedb
mgfs = GridFS(filedb)
out = mgfs.get(ObjectId(oid))
logger.info('Download file {0} by {1}'.format(out.filename, current_usr['name']))
fileName = out.filename
extName = fileName.split('.')[-1]
base64Data = codecs.encode(out.read(), 'base64')
res['base64'] = "data:image/%s;base64,%s" % (extName, base64Data. decode ('utf-8'))
return res
以上代码提供了文件下载和Base64图片下载。
4.前端文件下载封装
由于前端需要在下载时携带令牌,所以需要将下载文件的操作封装为统一的函数,代码如下:
// 文件下载
const downloadConfig = {
baseURL: '',
timeout: 60000,
headers: {
'Content-Type': 'application/json'
},
responseType:'blob' // 接收类型: blob
}
const getResource = (oid) => {
let _id = loading()
// eslint-disable-next-line
const promise = new Promise((resolve, reject) => {
downloadConfig.headers['Authorization'] =
localStorage.getItem('token_type') + ' ' + localStorage.getItem('access_token')
axios.get(
'/qycommon_api/download/download_file/' + oid,
downloadConfig
).then((response) => {
loaded(_id)
resolve(response)
}).catch((error) => {
loaded(_id)
console.log(error)
// 对error中的数据进行处理
let status = error.response.status
let txt = errorToString(error)
// 弹出错误信息
if (status === 401) {
swal({
title: status.toString(),
text: txt,
icon: 'error',
button: "确定",
}).then((value) => {
console.log(value)
window.top.location.href = '/index.html'
})
} else {
swal(status.toString(), txt, 'error')
}
// reject(error)
})
})
return promise
}
// 图片下载 base64
const base64Config = {
baseURL: '',
timeout: 60000,
headers: {
'Content-Type': 'application/json'
},
responseType: 'json'
}
const getBase64 = (oid) => {
let _id = loading()
// eslint-disable-next-line
const promise = new Promise((resolve, reject) => {
base64Config.headers['Authorization'] =
localStorage.getItem('token_type') + ' ' + localStorage.getItem('access_token')
axios.get(
'/qycommon_api/download/download_base64/' + oid,
base64Config
).then((response) => {
loaded(_id)
resolve(response)
}).catch((error) => {
loaded(_id)
console.log(error)
// 对error中的数据进行处理
let status = error.response.status
let txt = errorToString(error)
// 弹出错误信息
if (status === 401) {
swal({
title: status.toString(),
text: txt,
icon: 'error',
button: "确定",
}).then((value) => {
console.log(value)
window.top.location.href = '/index.html'
})
} else {
swal(status.toString(), txt, 'error')
}
// reject(error)
})
})
return promise
}
对应于后端,前端的文件下载封装也设计了两个函数:文件下载和Base64图片下载。
5.前端文件下载代码
// 下载头像文件
download_avatar_file (oid) {
// getResource(oid).then((response) => {
// this.avatar_file_data = window.URL.createObjectURL(response.data)
// })
getBase64(oid).then((response) => {
this.avatar_file_data = response.data.base64
})
},
对于下载的文件如果需要显示在页面上,则需要使用 window.URL.createObjectURL() 函数;
如果是 Base64 图像,直接将返回的 Base64 字符串赋值给相应的变量即可。