1.场景
服务端将文件内容或图片以流的形式发送给客户端,客户端实现点击按钮下载文件的功能,需要注意的是服务端可能返回文件流以外的错误情况,客户端需要进行判断,正确下载文件或做出错误提醒。
2.后端实现
后端以golang gin为例,实现简单的下载功能:
r.POST("/download", func(ctx *gin.Context) { var attachment models.Attachment if err := ctx.ShouldBind(&attachment); err != nil { ctx.JSON(http.StatusOK, gin.H{"msg": "参数格式错误!","code": 202}) return } if attachment.Path == nil { ctx.JSON(http.StatusOK, gin.H{"msg": "文件不存在!","code": 202}) } else { ctx.File(attachment.Path) } })
3.前端实现
根据后端的返回情况可以分为两类,一类是正常的文件流返回结果,第二类是JSON格式的错误消息提醒。
前端处理时有两个点需要特别注意:第一点是如果封装过axios响应拦截器且进行过数据解构返回的需要进行一下特殊的处理,因为实际的响应体对象即为文件数据。第二点是需要将axios配置中responseType的类型设置为arraybuffer。
文件流下载基本原理:
- 1.从服务器获取数据流。
- 2.将数据流转换成Blob对象。
- 3.创建一个URL指向该Blob对象。
- 4.创建一个a标签,设置其href属性为该URL,download属性为文件名。
- 5.模拟点击a标签,触发文件下载。
export const reqDownloadAttachment = (data: any) => request.post<any, any>(API.AttACHMENT_DOWNLOAD, data, { responseType: 'arraybuffer' }) // 一定要设置responseType为arraybuffer const downLoadFile = async (row:any) => { const res = await reqDownloadAttachment(row) try { const enc = new TextDecoder('utf-8') const data = JSON.parse(enc.decode(new Uint8Array(res.data))) // 尝试将 arraybuffer 转换为 json if (data.code !== 200) { // 能正常转换,说明返回的就是json数据 ElMessage.error(data.msg || data.error || '下载文件失败!') } } catch (error) { // 转换失败,说明返回的是文件流 将其转换为 Blob 对象并触发a标签的下载 const blob = new Blob([res.data], { type: res.headers['content-type'] || 'application/octet-stream' }) const a = document.createElement('a') a.style.display = 'none' a.href = URL.createObjectURL(blob) a.download = row.origin_name // 文件名 document.body.appendChild(a) a.click() document.body.removeChild(a) } }