前端 javascript 实现文件下载的示例


Posted in Javascript onNovember 24, 2020

在 html5 中,a 标签新增了 download 属性,包含该属性的链接被点击时,浏览器会以下载文件方式下载 href 属性上的链接。示例:

<a href="https://www.baidu.com" rel="external nofollow" download="baidu.html">下载</a>

1. 前端 js 下载实现与示例

通过 javascript 动态创建一个包含 download 属性的 a 元素,再触发点击事件,即可实现前端下载。

代码示例:

function download(href, title) {
    const a = document.createElement('a');
    a.setAttribute('href', href);
    a.setAttribute('download', title);
    a.click();
}

说明:

  • href 属性设置要下载的文件地址。这个地址支持多种方式的格式,因此可以实现丰富的下载方法。
  • download 属性设置了下载文件的名称。但 href 属性为普通链接并且跨域时,该属性值设置多数情况下会被浏览器忽略。

1.1 普通连接下载示例

// 下载图片
download('https://lzw.me/images/gravatar.gif', 'lzwme-gravatar');
// 下载一个连接
download('https://lzw.me', 'lzwme-index.html');

1.2 href 为 data URIs 示例
data URI 是前缀为 data:scheme 的 URL,允许内容创建者在文档中嵌入小文件。数据URI由四个部分组成:前缀(数据:),指示数据类型的MIME类型,如果非文本则为可选的base64令牌,数据本身:

data:[<mediatype>][;base64],<data>

链接的 href 属性为 data URIs 时,也可以实现文件内容的下载。示例:

download('data:,Hello%2C%20World!', 'data-uris.txt');
download('data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D', 'data-uris.txt');

1.3 canvas 下载示例
对于 canvas 可以通过 toDataURL 方法取得 data URIs 格式的内容。

1.4 二进制内容下载
URL.createObjectURL 方法会根据传入的参数创建一个指向该参数对象的 URL。新的对象 URL 指向执行的 File 对象或者是 Blob 对象。

URL.createObjectURL 的参数是 File 对象或者 Blob 对象,File 对象也就是通过 input[type=file] 选择的文件,Blob 对象是二进制数据。

将URL.createObjectURL 返回值设为 href 属性的值,即可实现二进制内容下载。示例:

const content = 'Welcome to lzw.me!';
const blob = new Blob([content]);
const href = URL.createObjectURL(blob);
download(href, 'download-text.txt');
URL.revokeObjectURL(href);

1.5 前端下载方法示例
综合上述讨论,这里给出一个前端实现下载的 saveAs 方法的 TypeScript 示例:

/**
 * 通过创建 a dom 对象方式实现前端文件下载
 * @param href 要下载的内容链接。当定义了 toBlob 时,可以为纯文本或二进制数据(取决于 toBlob 格式
 * @param fileName 下载后的文件名称
 * @param toBlob 如设置该参数,则通过 blob 方式将 href 转换为要保存的文件内容,该参数将入参为 new Blob([href], toBlob) 的第二个参数
 * @example
 * ```js
 * saveAs('abc', 'abc.txt', {});
 * saveAs('data:,Hello%2C%20World!', 'hello.txt');
 * saveAs('https://lzw.me/images/avatar/lzwme-80x80.png', 'lzwme-logo.png');
 * ```
 */
export function saveAs(href: string | Blob, fileName?: string, toBlob?: PlainObject) {
 const isBlob = href instanceof Blob || toBlob;
 if (!fileName && typeof href === 'string' && href.startsWith('http')) {
  fileName = href.slice(href.lastIndexOf('/') + 1);
 }
 fileName = decodeURIComponent(fileName || 'download');
 if (typeof href === 'string' && toBlob) href = new Blob([href], toBlob);
 if (href instanceof Blob) href = URL.createObjectURL(href);
 const aLink = document.createElement('a');
 aLink.setAttribute('href', href);
 aLink.setAttribute('download', fileName);
 aLink.click();
 // const evt = document.createEvent("HTMLEvents");
 // evt.initEvent("click", false, false);
 // aLink.dispatchEvent(evt);
 if (isBlob) setTimeout(() => URL.revokeObjectURL(aLink.href), 100);
 return aLink;
}

2.检测浏览器是否支持 download 属性

download 属性为 html5 新增内容,浏览器支持情况可参考:http://caniuse.com/#feat=download

<img src="https://lzw.me/wp-content/uploads/2017/04/a-download.png" alt="" width="879" height="346" class="aligncenter size-full wp-image-2330" />

判断浏览器是否支持该属性,只需要检测 a 标签是否存在 download 属性。示例:

const downloadAble = 'download' in document.createElement('a');

对于不支持的浏览器,只能另想他法或者予以降级处理了。

3.使用 serviceWorker 和 fetch API 代理实现

前端下载更多的需求是因为内容产生于前端。那么可以在后端实现一个这样的 API ,它在接收到前端发出的内容后返回下载格式的数据。这种实现就不存在浏览器兼容问题。

利用 serviceWorker 和 fetch API 截拦浏览器请求,只需实现好约定逻辑,也可实现这种功能需求。示例:

在页面中,通过 fetch API 构造请求:

fetch('lzwme.txt', {
    isDownload: true,
    body: {
        data: new Blob('hi!')
    }
})

在 serviceWorker 中,截拦附带 isDownload 头信息的请求,构造下载回应:

self.addEventListener('fetch', function(event) {
    const req = event.request;
    if (!req.headers.get('isDownload')) {
        retrun fetch(req);
    }
    const filename = encodeURIComponent(req.url);
    const contentType = req.headers.get('Content-Type') || 'application/force-download';
    const disposition = "inline;filename=" + filename + ";filename*=utf-8''" + filename
    const myBody = req.headers.get(body).data;
    event.respondWith(
        new Response(myBody, {
            headers: {
                'Content-Type': contentType,
                'Content-Disposition': disposition
            }
        })
    );
});

4 使用 ajax (xhr与fetch API) 方式下载服务器文件

以上主要讨论的是纯前端实现下载保存文件的方法。对于下载服务器文件,最简的方式就是 window.open(url) 和 location.href=url 了,但是其的弊端也很明显,当出错时整个页面都会挂掉,而且也无法获得下载状态与进度,下载时间稍长时体验相当不好。

下面介绍一下使用 xhr 和 fetch API 实现文件下载的方法。其主要思路为:将请求结果设为 Blob 类型,然后采用前端下载保存 Blob 类型数据的方式实现下载。

4.1 使用 xhr 下载远程服务器文件
代码示例:

/** 前端下载/保存文件 */
function saveAs(href, fileName) {
  const isBlob = href instanceof Blob;
  const aLink = document.createElement('a');
  aLink.href = isBlob ? window.URL.createObjectURL(href) : href;
  aLink.download = fileName;
  aLink.click();
  if (isBlob) setTimeout(() => URL.revokeObjectURL(aLink.href), 100);
}
function xhrDownload(url, options = {}) {
  options = Object.assign({ method: 'get', headers: {} }, options);
  return new Promise((reslove, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.responseType = 'blob'; // options.responseType;
    if (options.headers) {
      for (const key in options.headers) xhr.setRequestHeader(key, options.headers[key]);
    }
    xhr.onload = () => {
      // 从 Content-Disposition 中获取文件名示例
      const cd = xhr.getResponseHeader('Content-Disposition');
      if (cd && cd.includes('fileName') && !options.fileName) options.fileName = cd.split('fileName=')[1];
      options.fileName = decodeURIComponent(options.fileName || 'download-file');
      if (+xhr.status == 200) {
        saveAs(xhr.response, options.fileName);
        reslove(options.fileName);

使用 fecth API 下载远程服务器文件

function fetchDownload(url, options = {}) {
  options = Object.assign({ credentials: 'include', method: 'get', headers: {} }, options);
  return fetch(url, options).then(response => {
    return response.blob().then(blob => {
      if (!blob || !blob.size) return Promise.reject('empty');
      // 从 Content-Disposition 中获取文件名示例
      const cd = response.headers.get('Content-Disposition');
      if (cd && cd.includes('fileName') && !options.fileName) options.fileName = cd.split('fileName=')[1];
      options.fileName = decodeURIComponent(options.fileName || 'download-file');
      saveAs(blob, options.fileName);
      return options.fileName;
    });
  });
}
// 测试
fetchDownload('https://lzw.me/images/avatar/lzwme-80x80.png', {
    // method: 'post',
    // headers: {
    //   'Content-Type': 'application/json'
    // },
    // body: JSON.stringify({
    //   pageSize: 100000,
    //   startPage: 0
    // })
  })

以上就是前端 javascript 实现文件下载的示例的详细内容,更多关于JavaScript 文件下载的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
基于Jquery的跨域传输数据(JSONP)
Mar 10 Javascript
实现点击列表弹出列表索引的两种方式
Mar 08 Javascript
Jquery选中或取消radio示例
Sep 29 Javascript
js动态控制table的tr、td增加及删除的具体实现
Apr 30 Javascript
jquery仿搜索自动联想功能代码
May 23 Javascript
Jquery动态替换div内容及动态展示的方法
Jan 23 Javascript
11种ASP连接数据库的方法
Sep 18 Javascript
jQuery实现div拖拽效果实例分析
Feb 20 Javascript
微信小程序之多列表的显示和隐藏功能【附源码】
Aug 06 Javascript
移动端手指操控左右滑动的菜单
Sep 08 Javascript
three.js利用射线Raycaster进行碰撞检测
Mar 12 Javascript
JavaScript WeakMap使用详解
Feb 05 Javascript
如何使用 JavaScript 操作浏览器历史记录 API
Nov 24 #Javascript
JavaScript实现鼠标移入随机变换颜色
Nov 24 #Javascript
原生js实现表格循环滚动
Nov 24 #Javascript
浅析VUE防抖与节流
Nov 24 #Vue.js
解决vue页面刷新,数据丢失的问题
Nov 24 #Vue.js
Js利用正则表达式去除字符串的中括号
Nov 23 #Javascript
jQuery实现动态操作table行
Nov 23 #jQuery
You might like
GreyBox技术总结(转)
2010/11/23 Javascript
jQuery学习笔记之DOM对象和jQuery对象
2010/12/22 Javascript
autoPlay 基于jquery的图片自动播放效果
2011/12/07 Javascript
基于JQuery的一句话搞定手风琴菜单
2012/09/14 Javascript
jquery中的过滤操作详细解析
2013/12/02 Javascript
编写高质量JavaScript代码的基本要点
2016/03/02 Javascript
JS无缝滚动效果实现方法分析
2016/12/21 Javascript
JavaScript数据结构之广义表的定义与表示方法详解
2017/04/12 Javascript
React学习笔记之列表渲染示例详解
2017/08/22 Javascript
基于JavaScript实现带数据验证和复选框的表单提交
2017/08/23 Javascript
jQuery添加新内容的四个常用方法分析【append,prepend,after,before】
2019/03/19 jQuery
JavaScript函数式编程(Functional Programming)箭头函数(Arrow functions)用法分析
2019/05/22 Javascript
koa2服务端使用jwt进行鉴权及路由权限分发的流程分析
2019/07/22 Javascript
JavaScript Canvas编写炫彩的网页时钟
2019/10/16 Javascript
Vue 使用beforeEach实现登录状态检查功能
2019/10/31 Javascript
[07:39]第一届亚洲邀请赛回顾视频
2017/02/14 DOTA
Python验证码识别的方法
2015/07/10 Python
Python爬虫实例爬取网站搞笑段子
2017/11/08 Python
Python中摘要算法MD5,SHA1简介及应用实例代码
2018/01/09 Python
基于numpy.random.randn()与rand()的区别详解
2018/04/17 Python
python可视化爬虫界面之天气查询
2019/07/03 Python
django使用xadmin的全局配置详解
2019/11/15 Python
Python程序控制语句用法实例分析
2020/01/14 Python
python使用dlib进行人脸检测和关键点的示例
2020/12/05 Python
定义css设备类型-Media Queries图表简介及使用方法
2013/01/21 HTML / CSS
C#基础面试题
2016/10/17 面试题
高三自我鉴定
2013/10/23 职场文书
成功的餐厅经营创业计划书
2014/01/15 职场文书
会计学专业学生的求职信范文
2014/01/27 职场文书
连锁酒店店长职责范本
2014/02/13 职场文书
七一党日活动总结
2014/07/08 职场文书
物业工程部主管岗位职责
2015/04/16 职场文书
婚礼领导致辞大全
2015/07/28 职场文书
宣传部部长竞选稿
2015/11/21 职场文书
创业计划书之奶茶店开店方案范本!
2019/08/06 职场文书
jquery插件实现代码雨特效
2021/04/24 jQuery