JS前端创建html或json文件并浏览器导出下载

这篇文章发布于 2017年07月5日,星期三,02:03,归类于 Canvas相关, JS实例。 阅读 142324 次, 今日 3 次 48 条评论

 

一、HTML与文件下载

如果希望在前端侧直接触发某些资源的下载,最方便快捷的方法就是使用HTML5原生的download属性,例如:

<a href="large.jpg" download>下载</a>

具体介绍可参考我之前的文章:“了解HTML/HTML5中的download属性”。

但显然,如果纯粹利用HTML属性来实现文件的下载(而不是浏览器打开或浏览),对于动态内容,就无能为力。

例如,我们对页面进行分享的时候,希望分享图片是页面内容的实时截图,此时,这个图片就是动态的,纯HTML显然是无法满足我们的需求的,借助JS和其它一些HTML5特性,例如,将页面元素转换到canvas上,然后再转成图片进行下载。

但本文要介绍的下载不是图片的下载,而是文本信息的下载,所需要使用的HTML特性不是canvas,而是其它。

二、借助HTML5 Blob实现文本信息文件下载

如果对Blob不了解,可以先看看我好些年之前写的“理解DOMString、Document、FormData、Blob、File、ArrayBuffer数据类型”一文。

原理其实很简单,我们可以将文本或者JS字符串信息借助Blob转换成二进制,然后,作为<a>元素的href属性,配合download属性,实现下载。

代码也比较简单,如下示意(兼容Chrome和Firefox):

var funDownload = function (content, filename) {
    // 创建隐藏的可下载链接
    var eleLink = document.createElement('a');
    eleLink.download = filename;
    eleLink.style.display = 'none';
    // 字符内容转变成blob地址
    var blob = new Blob([content]);
    eleLink.href = URL.createObjectURL(blob);
    // 触发点击
    document.body.appendChild(eleLink);
    eleLink.click();
    // 然后移除
    document.body.removeChild(eleLink);
};

其中,content指需要下载的文本或字符串内容,filename指下载到系统中的文件名称。

万般言语不达意,一枚实例来走心。

您可以狠狠地点击这里:基于funDownload实现的html格式文件下载demo

点击“下载”按钮,会把文本域中的内容全部作为一个.html后缀文件下载下来,各流程效果如下面几张图:

下载按钮点击示意

出现下载确认框(根据浏览器的设置不同也可能直接下载),然后名称默认就是test.html

默认就是test.html名称

然后对应保存目录就多了个类似下图的文件:

保存好的test.html文件截图示意

双击该test.html文件可以在浏览器中正常浏览,说明,保存信息无误。

test.html在浏览器中访问的效果

触发下载的JS代码就几行:

button.addEventListener('click', function () {
    funDownload(textarea.value, 'test.html');	
});

三、借助Base64实现任意文件下载

对于非文本文件,也是可以直接JS触发下载的,例如,如果我们想下载一张图片,可以把这张图片转换成base64格式,然后下载。

代码示意:

var funDownload = function (domImg, filename) {
    // 创建隐藏的可下载链接
    var eleLink = document.createElement('a');
    eleLink.download = filename;
    eleLink.style.display = 'none';
    // 图片转base64地址
    var canvas = document.createElement('canvas');
    var context = canvas.getContext('2d');
    var width = domImg.naturalWidth;
    var height = domImg.naturalHeight;
    context.drawImage(domImg, 0, 0);
    // 如果是PNG图片,则canvas.toDataURL('image/png')
    eleLink.href = canvas.toDataURL('image/jpeg');
    // 触发点击
    document.body.appendChild(eleLink);
    eleLink.click();
    // 然后移除
    document.body.removeChild(eleLink);
};

四、结束语

不止是.html文件,.txt, .json等只要内容是文本的文件,都是可以利用这种小技巧实现下载的。

在Chrome浏览器下,模拟点击创建的<a>元素即使不append到页面中,也是可以触发下载的,但是在Firefox浏览器中却不行,因此,上面的funDownload()方法有一个appendChildremoveChild的处理,就是为了兼容Firefox浏览器。

download属性从Edge13开始支持,理论上,edge也应该支持直接JS触发的浏览器文件下载,但我手头上并无相关浏览器,无法确定真实情况如何,欢迎有条件的小伙伴帮忙测下告知结果。

感谢arrow的反馈:

edge下载完就生成一个文件,类似GUID命名的,加个后缀就正常了。

就这些吧,感谢阅读!

(本篇完)

分享到:


发表评论(目前48 条评论)

  1. 666说道:

    鑫哥,URL.revokeObjectURL()需要配合URL.createObjectURL()使用吗?

  2. 坐地吸土说道:

    如果是跨域的 mp4 url 有什么办法直接下载嘛

    • 我只想当个混子说道:

      只要涉及跨域,前端就无法独立解决,所以,你这个问题需要让运维或者后台协助。
      1. 让运维代理url地址
      2. 让后台下载后,再转发给你

  3. 小J说道:

    鑫哥这里的宽高没用上,需要用上才能得到完整的图片
    var width = domImg.naturalWidth;
    var height = domImg.naturalHeight;

  4. ght说道:

    你好,用创建blob方法下载下来的文件,打开显示文件损坏是什么问题呢

  5. peanut说道:

    能否纯前端生成word呢

  6. a57520说道:

    http:\/\/zhehetest.oss-cn-hangzhou.aliyuncs.com/file/2020/06/03/14期业绩结算单状态码.txt

    这个要怎么下载。。。怎么试都下载不下来==

  7. qq说道:

    如果后台只给个路径,这个路径可以用浏览器直接打开,
    后台也不处理跨域,
    怎么才能进行这个文件的下载呢 ,
    这是个以 .log为后缀的文件

  8. 小小白说道:

    请问,纯前端有办法下载视频吗?我们项目之前用的就是前端给后端提供URL,后端根据URL去下载,但是有的视频文件特别大1G左右,就会造成请求超时,下载失败,现在向转到前端自行下载,不知是否可行?还有URL地址和前端项目不在一个服务器上,存在跨域。。

  9. 呵呵说道:

    如果是服务器上的静态托管文件呢? 只有url, 怎么才能获取到文件内容呢? 只能通过ajax请求文件url获取到内容吗?

  10. 大哥,苹果手机怎么下载说道:

    苹果手机不支持download

    • 大哥,苹果手机怎么下载说道:

      跪求解答,我已经找了好久都不知道苹果手机怎么下载文件

  11. 破锣说道:

    请问移动端,有什么解决方案吗?

  12. 南墙猿人说道:

    旭哥,是canvas.toDataURL(),canvas的context对象没有这个方法,这个方法是canvas节点的。

  13. 下载css和js说道:

    如果在html中引入了外部css和js,怎么下载html的时候连同也下载css和js

  14. 跨域的图片貌似下载不了啊说道:

    Uncaught DOMException: Failed to execute ‘toDataURL’ on ‘HTMLCanvasElement’: Tainted canvases may not be exported.

    • 张 鑫旭说道:

      需要图片服务器配置accept header,可参见这篇文章:https://www.zhangxinxu.com/wordpress/2018/02/crossorigin-canvas-getimagedata-cors/

  15. 菜鸡coder说道:

    txt中文本的格式是怎么控制的呢,苹果端中下载的txt 格式是对齐的 但是window中txt 就乱了

  16. szh说道:

    请问下为什么网络路径就直接在另一个页面打开了呢
    怎么解决呢

  17. 蜗牛说道:

    那么上传呢?就是在本地生成或者本地有一个html文件,然后动态写入内容进行上传。

  18. 言灵说道:

    文件下载似乎不能指定utf-8编码呢,不知道是否有办法在生成的时候指定文件的编码格式呢?求大神解答

  19. 冬瓜不圆说道:

    请问一下,在移动端好像不行呢,点击没反应,怎么破

  20. 跳动的青春说道:

    解决了我的问题,点赞

  21. hi说道:

    旭哥,是canvas.toDataURL()

  22. hello说道:

    怎么在代码里面指定目录,然后自动下载,不用弹框出来进行选择,

  23. 王熊喵说道:

    这个办法解决了通过post下载文件的问题,感动涕零
    从fetch返回response对象的blob方法可以获取到后端的文件流,then回调的参数就是blob对象,用headers Content-Disposition 可以判断文件名,直接就能下载了,爽到不行
    ···
    response.blob().then((blob) => {
    //create donwload
    }
    ···

  24. 善良的小伟说道:

    您好。请问如果我需要下载的是js文件怎么办呢?该js文件内容只是简单的对象嵌套。我尝试改变MIME类型,但发现出来的还是[Object Object]。用JSON转成字符串是可以实现的,但我希望原文下载。需要怎么做呢?

  25. boss说道:

    大神,我就想问问,浏览器能做到分片下载大文件?能实现串行下载,一个文件下载完下载另一个文件?

    • 张 鑫旭说道:

      可以,可以直接对文件数据进行slice~

      • 手机端如何通过文件流下载文件说道:

        我用浏览器已经测试成功了,能把文件下载下来。到了手机上把文件流读完后就弹出是否下载的对话框,点下载就提示下载失败,也没个下载失败的原因。

  26. 妖妖说道:

    太短了

  27. 小妖说道:

    请问一下。IE 有其他方式可以代替实现吗?

  28. 南华华说道:

    这个事儿我没记错的话FileSaver.js就是这么实现的

  29. 胡衍生说道:

    如果是比较大的文件,如视频、音频文件就没法做了吧?

  30. 顶点小说说道:

    可不可以把数据库的内容,导成excel格式的

  31. nicholasurey说道:

    生成了一个叫“A1DEA3AF-8977-4064-A5D8-5B17DC9E44EB”的无后缀文件

  32. Hazel说道:

    张大哥你好,入坑前端以来就一直关注你的博客,平时遇到CSS方面的难题第一个想到的就是打开你的博客看看有没有类似研究,不得不说这方面你已经走在了非常前沿,钻研得很透彻。

    最近我遇到一个关于position:fixed 的有趣现象,这里想和你讨教一下。我查阅了国内外能触及到的资料,其定义解释都是,这个属性是元素相对于viewport进行绝对定位。

    但是!有趣的是,当我把left的值设置为auto或删掉left属性时,奇迹发生了!该元素竟然位于父元素的最边缘!此处left作为举例,经尝试,right也是同样的效果。
    废话不多说,请看代码
    链接在此
    https://codepen.io/HazelZ/pen/GEGOqv

    不知该如何解释这一现象,还请大哥指正

  33. arrow说道:

    edge
    下载完就生成一个文件,类似GUID命名的
    加个后缀就正常了

  34. Margox说道:

    旭哥,想知道HTML转canvas,有哪些实现方法呢?我目前只知道一个借助svg的foreignObject来实现的办法