likes
comments
collection
share

H5中的文档和视频下载国内的手机浏览器有很多,再加上各个品牌自带的浏览器,以及Android上的chrome,每种浏览器

作者站长头像
站长
· 阅读数 18

  国内的手机浏览器有很多,再加上各个品牌自带的浏览器,以及Android上的chrome,每种浏览器的表现都不尽相同。如果想要在H5中,实现文档和视频的下载,应该怎么做呢?

需求来啦

  我们业务线有多种设备,在运营后台可以上传每种设备的介绍文档、使用文档、配套视频等文件(我们使用的是腾讯云,上传到存储桶中)。官网上有一个指向app下载和这些文件的二维码(实际就是一个H5地址,页面上有下载app的按钮,也可进入各个设备的文档、视频列表页)。现在想在列表项后面,加一个下载图标,用户可以把文件下载到本地。

1. a标签+download

  第一反应这不就是<a href="文件地址" download="文件名">icon-download</a>嘛,正好我们是上传到腾讯云存储桶的(允许跨域配置过了),返回的就是文件的访问地址,开整。

  本地开发和测试的时候,肯定还是在PC端,试了下,文档没啥问题(docx),视频不行(mp4)。首先我们要知道,a标签的download属性是干嘛的:假如说一个文件,浏览器可以直接打开,比如图片(通过请求头中Content-Type中的MIME类型,识别数据类型,做对应处理),浏览器的默认行为是打开这个文件;download就是告诉浏览器,我的目的是下载,你别打开,直接下载就行了。但是,假如这种文件类型,浏览器本身就打不开,那它的默认行为自然就是下载。这其实和使用window.open方式是一样的。

  为啥视频不行呢,难道是兼容性问题?我就去看了下caniuse。瞥了一眼,感觉这兼容性应该没啥问题啊。

H5中的文档和视频下载国内的手机浏览器有很多,再加上各个品牌自带的浏览器,以及Android上的chrome,每种浏览器 然后我看到下面的Known issues(3),原来download真的有问题:跨域限制

H5中的文档和视频下载国内的手机浏览器有很多,再加上各个品牌自带的浏览器,以及Android上的chrome,每种浏览器 我们这些文件都是在腾讯云上,域名肯定是不一样的,那只能用其他方法了。

2.fetch+URL.createObjectURL+a标签

URL:createObjectURL() 静态方法 - Web API | MDN

URL 接口的 createObjectURL()  静态方法创建一个用于表示参数中给出的对象的 URL 的字符串。

URL 的生命周期与其创建时所在窗口的 document 绑定在一起。新对象 URL 代表指定的 File对象或 Blob对象。

要释放对象 URL,请调用 revokeObjectURL() createObjectURL() 这个方法相信大家都用过,通常项目中需要下载一些业务数据的时候,应该都是用的这个方法,把后台传过来的blob,构建出来一个地址,然后用a标签下载

fetch(fileURL, {mode:'cors'}).then(async res => {
    const blob = await res.blob()
    const downloadURL = URL.createObjectURL(blob)
    // 构建a标签
    const a = document.createElement('a')
    a.setAttribute('href', downloadURL)
    a.setAttribute('download', filename)
    a.click()
    URL.revokeObjectURL(downloadURL)
})

用了这种方式,试了下,都可以下载了,但是在手机浏览器上,就不行了。 我用小米自带浏览器,文档和视频都可以;用qq浏览器,文档可以,视频会生成下载任务,但是不会去下载;用oppo自带浏览器,文档和视频都只会生成下载任务,不会去下载;其他浏览器也都大差不差,下载任务可以生成,但是不会下载;有的是只能下载一部分,进度就停了。chrome for Android也可以,safari有点记不得了。

  • 这个不清楚是不是手机浏览器有什么限制,如果有jy知道,希望回复告知!

3.Content-Disposition: attachment;filename="xxx"

  这个之前还真不清楚,问了下同组的同事。正好用得是腾讯云,看了下腾讯云的文档对象存储-下载对象,看到了里面的一个例子:

// 摘抄自例子,实际使用过程中都是使用临时密钥
cos.getObjectUrl({
    Bucket: config.Bucket,
    Region: config.Region,
    Key: item.Key,
    }, function(err, data) {
        if (err) return console.log(err);
        setTimeout(() => {
            var downloadUrl = data.Url + (data.Url.indexOf('?') > -1 ? '&' : '?') + 
        'response-content-disposition=attachment;filename=xxx'; // 补充强制下载的参数

        window.open(downloadUrl); 
        // 这里是新窗口打开 url,如果需要在当前窗口打开,可以使用隐藏的 iframe 下载,
        // 或使用 a 标签 download 属性协助下载
        }, 500);
})

腾讯云提供了一个通过在地址后面拼接参数,可以使响应头上带上content-disposition: attachment的方法(上传文件时返回的地址,直接拼接是不可以的,没有用,必须得调用getObjedctUrl方法返回的地址才行,腾讯云做了限制应该) 使用这个方法,绝大部分浏览器都可以了,文档、视频都能正常下载,但是百度浏览器不行,这就不知道用什么法子了。jy们有什么建议没

总结

  1. Content-Disposition: attachment适用性最广,借助浏览器的流式传输,但是要后端或运维进行改造
  2. a标签download不跨域的情况下,最简单
  3. 请求+URL.createObjectURL+动态a标签手机浏览器上问题比较大,PC端没问题,但是会增加内存占用,需要及时释放,并且createObjectURL随文件增大时间增加(这玩意是同步的)

后记 由于我们需要调接口来获取腾讯云临时密钥,这个页面访问又不需要登录,后端认为有安全隐患,不能开白名单,所以最终说服产品,文档和视频就叫用户直接在浏览器上看就完事了,反正手机浏览器都支持;某些可以通过createObjectURL下载的浏览器,前端判断下,再显示下载图标😂。所以说,说服产品最重要💪

转载自:https://juejin.cn/post/7411480128528433206
评论
请登录