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

发布于 1周前 作者 zlyuanteng 来自 Go语言

最近在尝试实现一个功能,从 k8s Container 中导出文件下载。目前已经能够获取到文件流,但是在返回给前端时出现了问题,我尝试直接使用 io.Copy 将文件流 Copy 到 response 的 responseWriter 中,前端使用 fetch 请求下载。但是该模式会有一个问题,就是触发下载请求后,浏览器会一直等待后端返回数据,在文件流加载好之后直接提示下载完成。而不像平常下载那样有下载进度条。这种情况我推测是 response 是完全加载好文件数据流后再一次返回,使用 wireshark 抓包时可能因为一次数据量过大( 500 多 MB )直接卡死。
不怎么懂前端,我想知道有没有较好的方式能将文件流直接返回给前端实现下载,使用 ws 下载也行。
Golang Go语言+Vue.js 如何较好的实现 Web 下载大文件?


更多关于Golang Go语言+Vue.js 如何较好的实现 Web 下载大文件?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

33 回复

需要先获取文件大小后通过 Content-Length 返回给客户端,客户端才能计算出进度

更多关于Golang Go语言+Vue.js 如何较好的实现 Web 下载大文件?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你可以尝试使用 http.ServeFile 接口来返回文件内容,这个接口中已正确的处理了 Content-Type 和 Content-Length 字段

io.Copy 每次只写入 32KB 的数据,应该就是楼上说的没有返回 Content-Length 导致客户端计算不出进度

浏览器也有 steam ,可以试试这个库,不过可能会有兼容性问题
https://github.com/jimmywarting/StreamSaver.js

您好,Content-Length 这个我之前确实没有处理,但是我设置 Content-Length 后还是之前的问题,点击后浏览器没反应,大概 5 秒后之前跳出下载完成的提示。至于 http.ServeFile ,我之前使用过,效果和 io.Copy 是一样的,仍然是触发下载后几秒钟没反应,然后直接显示下载文件完成。
我现在有点怀疑是前端的请求问题,前端我是用的 fetch+创建 a 标签的下载方式,这种用法是不是有问题?

您好,我使用 http.ServeFile 和手动设置 Content-Length 后还是一样的情况,点击下载后没反应,大概几秒后直接弹出下载完成的提示。

fetch 是脚本中的请求怎么能触发浏览器下载?浏览器下载不是 a.click()触发吗?

服务器返回 http 206 客户端进度看 onprogress

fetch 请求后,document createElement 一个 display = none 的 a 标签,然后调用 a.click()

为什么会用 fetch 请求?

应该:

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

ps:伪代码

哪你这个 fetch 和 a 标签怎么关联起来?你这步下载就错了吧。


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);

代码在 12# 前端这块不是太清楚

大概率 fetch 的问题,前端要么直接跳转下载链接下载要么用 fs 去下载,fetch 那肯定会获取完数据才能处理,容易卡死

你这样写肯定是要等脚本下载完成才会弹下载框的。压根没有进度条相关的代码。

你这是请求结束后才交给浏览器下载保存,主要的网络耗时都在交给浏览器之前,交给浏览器之后基本没有网络流量;所以会有你说的现象

你这个相当于前端把数据全部拉下来丢内存再下载 效率可想而知

createObjectURL 就是这样的,直接用#10 的方法就不会这样了

为什么不直接

let a = document.createElement(‘a’)
a.href= url
# 这个 url 是你上面,fetch 的 url
# 为什么会在通过什么 blob 转换来转换去的?
a.click()



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




感谢,请问有这块的相关文档文章吗,我去看看

https://blogs.360.net/post/%E7%94%A8-filesystem-api-%E5%AE%9E%E7%8E%B0%E6%96%87%E4%BB%B6%E4%B8%8B%E8%BD%BD%E5%99%A8.html
如果非要用 js 下载可以通过 filesystem 的方式,现在有不少新生服务都喜欢这个模式,优点是比较灵活

感谢,我之前用 js 发请求主要是为了 header 添加信息处理鉴权,用 a.href 直接下载的话虽然解决了下载的问题,但会有权限的问题,我再看看这块。

添加鉴权的话你需要用 worker 方式拦截浏览器的下载请求,然后在 worker 中把 header 加上去

有鉴权需求可以试试成熟的 axios 请求库

你可以把 token 直接放到 URL 的查询参数里面啊

别处理下载流量过程,把下载交给浏览器来干,模拟一个 a 标签点击事件。

你好,我最近也遇到类似问题,似乎通过 xhr 模式(例如用 axios 或 fetach ),无论怎么写,总是要等待 response 获取完毕才能继续处理,如果使用 blob 模式,大文件就会有很大问题,res 过程中刷新页面就中断了,浏览器自身下载管理也无法接管下载,等 blob 构建完成实际上再弹出的浏览器下载框就快完成下载动作了。 现在策略是 acios 去请求下载的临时地址,这样后台能鉴权,后台认可后返回下载地址和地址和一个专用的下载 key ,key 存 cookie 中,再构造一个 a 标签(加上 download 属性),href 写为临时地址( rudownload/hdjkkdnf )然后点击 a 标签,这样后台监听 download 这个路由,就能取到临时地址和 cookie ,cokkie 用来鉴定权限,临时地址对应下载的资源(怎么对应的方法很多,例如临时地址和对应资源地址存到数据库), 后端返回文件即可。 不知道还有啥方法不,我能找到由浏览器来接管下载的方法就是这样了。

当然,其实也可以省掉前面一步,直接用 a 标签来下载,带上 cookie 进行认证

我用的也是类似这个模式,用户发送请求后,后端不执行下载而是根据信息返回一个临时 token ,前端获取到 token 再进行实际下载请求,我同时将这个实际下载请求从中间件中直接放行了,只是使用 token 进行校验。因为我的这个项目目前是公司内部使用,安全性和性能之类的要求不高,暂时就只使用这种方式了。
至于其他下载方式的话,如果不想专门为了下载文件引入其他系统,那做到这样我觉得差不多了。如果真有特别强烈的需求,那针对文件存储下载单独写一个服务,再提供一个中间件可能更好一些,不过我觉得是没必要了。

在使用Golang(Go语言)和Vue.js构建Web应用时,实现大文件下载需要特别注意性能和用户体验。以下是一些建议,帮助你较好地实现这一功能:

  1. 后端(Golang)处理

    • 流式传输:使用http.ResponseWriterWrite方法,将文件内容分块读取并写入响应中,避免一次性加载整个文件到内存中。
    • 设置响应头:正确设置Content-Dispositionattachment; filename="yourfile.ext",以便浏览器识别为下载请求。同时,设置Content-TypeContent-Length
    • 并发处理:对于高并发下载场景,考虑使用Go的协程来管理多个下载请求,确保服务器性能。
  2. 前端(Vue.js)处理

    • 触发下载:使用<a>标签的download属性,结合Blob对象和URL.createObjectURL,创建一个可下载的链接。
    • 进度显示:通过Ajax请求(如axios)的进度事件,实时更新下载进度给用户。注意,对于大文件,建议使用Range请求实现断点续传,Vue.js中可以通过axios的onDownloadProgress事件监听进度。
    • 错误处理:提供友好的错误提示,如网络错误、文件不存在等。
  3. 整体优化

    • 文件分片:对于超大文件,考虑实现文件分片下载,用户可以选择下载文件的某个部分。
    • CDN加速:如果可能,将大文件存储在CDN上,减少服务器负载,提高下载速度。

通过上述方法,可以有效实现Golang后端和Vue.js前端的大文件下载功能,同时保证良好的用户体验和服务器性能。

回到顶部