Go+Vue.js 如何较好的实现 Web 下载大文件?

2022-12-30 10:13:50 +08:00
 Frankcox
最近在尝试实现一个功能,从 k8s Container 中导出文件下载。目前已经能够获取到文件流,但是在返回给前端时出现了问题,我尝试直接使用 io.Copy 将文件流 Copy 到 response 的 responseWriter 中,前端使用 fetch 请求下载。但是该模式会有一个问题,就是触发下载请求后,浏览器会一直等待后端返回数据,在文件流加载好之后直接提示下载完成。而不像平常下载那样有下载进度条。这种情况我推测是 response 是完全加载好文件数据流后再一次返回,使用 wireshark 抓包时可能因为一次数据量过大( 500 多 MB )直接卡死。
不怎么懂前端,我想知道有没有较好的方式能将文件流直接返回给前端实现下载,使用 ws 下载也行。
3227 次点击
所在节点    Go 编程语言
32 条回复
lwch
2022-12-30 10:19:37 +08:00
需要先获取文件大小后通过 Content-Length 返回给客户端,客户端才能计算出进度
lwch
2022-12-30 10:25:46 +08:00
你可以尝试使用 http.ServeFile 接口来返回文件内容,这个接口中已正确的处理了 Content-Type 和 Content-Length 字段
kumoocat
2022-12-30 10:40:13 +08:00
io.Copy 每次只写入 32KB 的数据,应该就是楼上说的没有返回 Content-Length 导致客户端计算不出进度
asen001
2022-12-30 10:40:48 +08:00
浏览器也有 steam ,可以试试这个库,不过可能会有兼容性问题
https://github.com/jimmywarting/StreamSaver.js
Frankcox
2022-12-30 10:41:30 +08:00
@lwch 您好,Content-Length 这个我之前确实没有处理,但是我设置 Content-Length 后还是之前的问题,点击后浏览器没反应,大概 5 秒后之前跳出下载完成的提示。至于 http.ServeFile ,我之前使用过,效果和 io.Copy 是一样的,仍然是触发下载后几秒钟没反应,然后直接显示下载文件完成。
我现在有点怀疑是前端的请求问题,前端我是用的 fetch+创建 a 标签的下载方式,这种用法是不是有问题?
Frankcox
2022-12-30 10:46:17 +08:00
@kumoocat 您好,我使用 http.ServeFile 和手动设置 Content-Length 后还是一样的情况,点击下载后没反应,大概几秒后直接弹出下载完成的提示。
okakuyang
2022-12-30 10:51:21 +08:00
fetch 是脚本中的请求怎么能触发浏览器下载?浏览器下载不是 a.click()触发吗?
qscasdqwezxc
2022-12-30 10:57:33 +08:00
服务器返回 http 206 客户端进度看 onprogress
Frankcox
2022-12-30 10:57:34 +08:00
@okakuyang fetch 请求后,document createElement 一个 display = none 的 a 标签,然后调用 a.click()
timnottom
2022-12-30 11:00:26 +08:00
为什么会用 fetch 请求?

应该:

let a = document.createElement('a')
a.href= 'download link'
a.click()

ps:伪代码
okakuyang
2022-12-30 11:02:45 +08:00
@Frankcox 哪你这个 fetch 和 a 标签怎么关联起来?你这步下载就错了吧。
Frankcox
2022-12-30 11:02:47 +08:00
@timnottom
fetch(url,{
method: 'get',
responseType: 'blob'
}).then(res => res.blob()).then(data => {
const downloadURL = window.URL.createObjectURL(data);
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadURL;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(downloadURL);
Frankcox
2022-12-30 11:04:25 +08:00
@okakuyang 代码在 12# 前端这块不是太清楚
rekulas
2022-12-30 11:04:53 +08:00
大概率 fetch 的问题,前端要么直接跳转下载链接下载要么用 fs 去下载,fetch 那肯定会获取完数据才能处理,容易卡死
okakuyang
2022-12-30 11:05:43 +08:00
你这样写肯定是要等脚本下载完成才会弹下载框的。压根没有进度条相关的代码。
AlphaTr
2022-12-30 11:06:00 +08:00
@Frankcox 你这是请求结束后才交给浏览器下载保存,主要的网络耗时都在交给浏览器之前,交给浏览器之后基本没有网络流量;所以会有你说的现象
rekulas
2022-12-30 11:06:27 +08:00
@Frankcox 你这个相当于前端把数据全部拉下来丢内存再下载 效率可想而知
monkeyWie
2022-12-30 11:06:30 +08:00
createObjectURL 就是这样的,直接用#10 的方法就不会这样了
timnottom
2022-12-30 11:06:44 +08:00
@Frankcox 为什么不直接

let a = document.createElement('a')
a.href= url
# 这个 url 是你上面,fetch 的 url
# 为什么会在通过什么 blob 转换来转换去的?
a.click()
okakuyang
2022-12-30 11:06:54 +08:00
@Frankcox

a.href = downloadURL;
a.download = filename;
document.body.appendChild(a);
a.click();
这里 downloadURL 直接用你 fetch 里的 url 就行了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/905515

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX