前端 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 相关文章推荐
地址栏上的一段语句,改变页面的风格。(教程)
Apr 02 Javascript
javascript优先加载笔记代码
Sep 30 Javascript
一些相见恨晚的 JavaScript 技巧
Apr 25 Javascript
减少访问DOM的次数提升javascript性能
Feb 24 Javascript
Javascript中的几种URL编码方法比较
Jan 23 Javascript
JavaScript中循环遍历Array与Map的方法小结
Mar 12 Javascript
Validform验证时可以为空否则按照指定格式验证
Oct 20 Javascript
详解webpack 最简打包结果分析
Feb 20 Javascript
在微信小程序中使用图表的方法示例
Apr 25 Javascript
arctext.js实现文字平滑弯曲弧形效果的插件
May 13 Javascript
vue实现的多页面项目如何优化打包的步骤详解
Jul 19 Javascript
js实现炫酷光感效果
Sep 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
用Flash图形化数据(一)
2006/10/09 PHP
php读取文件内容的几种方法详解
2013/06/26 PHP
php获得用户ip地址的比较不错的方法
2014/02/08 PHP
PHP简单实现HTTP和HTTPS跨域共享session解决办法
2015/05/27 PHP
php过滤htmlspecialchars() 函数实现把预定义的字符转换为 HTML 实体用法分析
2019/06/25 PHP
ASP中进行HTML数据及JS数据编码函数
2009/11/11 Javascript
js 自动播放的实例代码
2013/11/19 Javascript
JQuery使用$.ajax和checkbox实现下次不在通知功能
2015/04/16 Javascript
javascript中attachEvent用法实例分析
2015/05/14 Javascript
JavaScript实现复制或剪切内容到剪贴板功能的方法
2016/05/23 Javascript
JS提示:Uncaught SyntaxError:Unexpected token ) 错误的解决方法
2016/08/19 Javascript
浅谈Node.js:Buffer模块
2016/12/05 Javascript
微信小程序实现发送验证码按钮效果
2018/12/20 Javascript
vue封装一个简单的div框选时间的组件的方法
2019/01/06 Javascript
nodejs微信开发之接入指南
2019/03/17 NodeJs
AjaxFileUpload.js实现异步上传文件功能
2019/04/19 Javascript
OpenLayer学习之自定义测量控件
2020/09/28 Javascript
详解Python中open()函数指定文件打开方式的用法
2016/06/04 Python
详解Python中的动态属性和特性
2018/04/07 Python
Python实现的堆排序算法示例
2018/04/29 Python
pycharm 主题theme设置调整仿sublime的方法
2018/05/23 Python
python2与python3中关于对NaN类型数据的判断和转换方法
2018/10/30 Python
pygame游戏之旅 添加键盘按键的方法
2018/11/20 Python
python实现随机漫步方法和原理
2019/06/10 Python
Python 运行.py文件和交互式运行代码的区别详解
2019/07/02 Python
python+opencv边缘提取与各函数参数解析
2020/03/09 Python
在python中使用pyspark读写Hive数据操作
2020/06/06 Python
python 基于opencv 绘制图像轮廓
2020/12/11 Python
Python-split()函数实例用法讲解
2020/12/18 Python
Python使用paramiko连接远程服务器执行Shell命令的实现
2021/03/04 Python
Feelunique美国:欧洲大型的在线美妆零售电商
2018/11/04 全球购物
什么是ESB?请介绍一下ESB?
2015/05/27 面试题
党员活动日总结
2014/05/05 职场文书
2015年企业工作总结范文
2015/04/28 职场文书
关于Javascript闭包与应用的详解
2021/04/22 Javascript
上个世纪50年代的可穿戴技术:无线电帽子
2022/02/18 无线电