Flask+vue+axios实现流式zip包传输
1. Flask部分
1.1 创建压缩包
class DirPackage:
@staticmethod
def pack_zip(from_path, to_path):
"""打包指定路径到某目录下面
:param from_path: 打包路径
:param to_path: .zip结尾的文件存放位置
"""
z = zipfile.ZipFile(to_path, 'w', zipfile.ZIP_DEFLATED)
for path, dirnames, filenames in os.walk(from_path):
fpath = path.replace(from_path, '')
for filename in filenames:
encode_name = filename.encode('utf-8').decode('utf-8')
z.write(os.path.join(path, filename),
os.path.join(fpath, encode_name))
z.close()
(file_path, file_name) = os.path.split(to_path)
return file_name
1.2 封装流式传输部分
class DownloadService:
@staticmethod
def download_zip_file(saver: DownloadDirFactory, user_name: str, token: str, **kwargs):
zip_file = os.path.join(saver.NORMAL_0, '{}_{}.zip'.format(user_name, token))
file_name = DirPackage.pack_zip(saver.NORMAL_1, zip_file)
def send():
with open(zip_file, 'rb') as file:
while True:
data = file.read(20 * 1024 * 1024) # 20M
if not data:
break
yield data
response = Response(send(), content_type='application/octet-stream')
response.headers["Access-Control-Expose-Headers"] = "Content-Disposition"
response.headers["Content-Disposition"] = 'attachment; filename={}'.format(file_name.encode('utf-8').decode('ISO-8859-1'))
return response
response需要设置Access-Control-Expose-Headers否则前台axios,post请求无法获取对应的文件名
1.3 路由接口
@staticmethod
@app.route("/app/multidownload", methods=["POST"])
def multi_batch_download():
uName, token = MultiBatchController.ParserTool.parser_name_token_from_request(request)
download_saver = MultiBatchController.MultiBatchTools.saver_copy_and_init(
MultiBatchController.DownloadSaveService, request, basepath)
return MultiBatchController.DownloadTools.download_zip_file(download_saver, uName, token)
2. vue前台
2.1 template
placeholder="ip" clearable v-model="user_info.user_name" style="width: 240px" />
2.2 JS部分
import axios from 'axios'
export default {
data () {
return {
user_info: {
user_name: '',
token: '12345'
},
url: 'http://192.168.31.200:50001/app/multidownload'
}
},
mounted () {
},
methods: {
// 获得处理好的东西
image_get () {
let param = new FormData()
param.append('user_name', 'tacom')
param.append('token', '12345')
let config = {
headers: { 'Content-Type': 'application/x-www-form-urlencoded; application/octet-stream' },
responseType: 'blob'
}
axios.post(this.url,
param, config
).then((res) => {
debugger
const blob = new Blob([res.data], { type: "application/zip" })
// let decode_data = Base64.decode(res.data)
let dispositon = decodeURI(res.headers['content-disposition'])
let fileName = dispositon.substring(dispositon.indexOf('filename=')+9, dispositon.length)
const elink = document.createElement('a')
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href)
document.body.removeChild(elink)
})
}
}
}
首先需要设置responseType为blob同时还在在headers中指明协议最后在创建blob对象的时候指名类别
3. Axios解码异常/压缩包损坏
我的异常是Mock拦截了blob数据,并强制转码为了str数据所以如果压缩包损坏/数据量不同,需要注意返回的类型是否正确上图注释掉mock模块前,responseType是空,request是MockXMLHttpRequest
相关链接
发表评论