查看原文
其他

文件生成需要后台服务?前端就能搞定!前端生成可下载文件详解

码中人 码农真经 2023-12-25

文章内容

  • 原理

    • Blob对象

    • MIME类型

    • 构建下载链接

  • 生成文件示例

    • 生成txt

    • 生成html

    • 生成css

    • 生成json

    • 生成csv/excel

    • 生成图

    • 生成pdf

  • 示例代码

一般而言,文件下载需要两个条件:

  1. 文件
  2. 文件地址(一个指向文件的链接)

这个文件可以是在服务器上的静态文件,也可以是服务器动态生成的。当然,也能由前端生成。以下为前端生成可下载文件的技术细节。



构建文件 Blob 对象


Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取。

Blob 由一个可选的字符串 type(通常是 MIME 类型)和 blobParts 组成 —— 一系列其他 Blob 对象,字符串和 BufferSource。

构造函数的语法为:

new Blob(blobParts, options);
  • blobParts 是 Blob/BufferSource/String 类型的值的数组。
  • options 可选对象:
    • type —— Blob 类型,通常是 MIME 类型,例如 image/png
    • endings —— 是否转换换行符,使 Blob 对应于当前操作系统的换行符(\r\n 或 \n)。默认为 "transparent"(啥也不做),不过也可以是 "native"(转换)。

一般只用到options的type类型:

const file = new Blob([blobData], { type: filetype });




MIME 类型


媒体类型(通常称为 Multipurpose Internet Mail Extensions  MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。

其通用结构为:

type/subtype

常见的类型有:

两种主要的 MIME 类型在默认类型中扮演了重要的角色:

  • text/plain 表示文本文件的默认值。一个文本文件应当是人类可读的,并且不包含二进制数据。
  • application/octet-stream 表示所有其他情况的默认值。一种未知的文件类型应当使用此类型。浏览器在处理这些文件时会特别小心, 试图防止、避免用户的危险行为.




构建下载链接


我们可以在 Javascript 中动态创建一个链接,通过 link.click() 模拟一个点击,然后便自动下载了。

1 URL.createObjectURL  创建资源地址指向

URL.createObjectURL 取一个 Blob,并为其创建一个唯一的 URL,形式为 blob:<origin>/<uuid>

浏览器内部为每个通过 URL.createObjectURL 生成的 URL 存储了一个 URL → Blob 映射。因此,此类 URL 很短,但可以访问 Blob

生成的 URL(即其链接)仅在当前文档打开的状态下才有效。它允许引用 <img><a> 中的 Blob,以及基本上任何其他期望 URL 的对象。

2 URL.revokeObjectURL 回收资源

不过它有个副作用。虽然这里有 Blob 的映射,但 Blob 本身只保存在内存中的。浏览器无法释放它。

在文档退出时(unload),该映射会被自动清除,因此 Blob 也相应被释放了。但是,如果应用程序寿命很长,那这个释放就不会很快发生。

因此,如果我们创建一个 URL,那么即使我们不再需要该 Blob 了,它也会被挂在内存中。

URL.revokeObjectURL(url) 从内部映射中移除引用,因此允许 Blob 被删除(如果没有其他引用的话),并释放内存。

在上面最后一个示例中,我们打算仅使用一次 Blob,来进行即时下载,因此我们立即调用 URL.revokeObjectURL(link.href)

而在前一个带有可点击的 HTML 链接的示例中,我们不调用 URL.revokeObjectURL(link.href),因为那样会使 Blob URL 无效。在调用该方法后,由于映射被删除了,因此该 URL 也就不再起作用了。

3 download 特性

download特性(attribute)强制浏览器下载而不是导航。



生成文件示例


生成.txt

生成.html

生成.css

生成.json

生成.csv

CSV是用UTF-8编码的,而EXCEL是ANSI编码,解决方法是在csv字符串前加上 \ufeff BOM 头。

生成图片

图像操作是通过 <canvas> 元素来实现的:

  1. 使用 canvas.drawImage 在 canvas 上绘制图像(或图像的一部分)。

  2. 调用 canvas 方法 .toBlob(callback, format, quality) 创建一个 Blob,并在创建完成后使用其运行 callback

这里推荐一个很好的工具,可以很方便将网页转换成图片。

html2canvas – Screenshots with JavaScript

html2canvas.js是一个将html dom转化成图片的 js 类库。

我用这个类库写个一个代码截图工具:

so easy...自制代码截图工具

http://codeimg.mzh.ren/

生成PDF文件

跟图片一样,js生成PDF文件也是基于Canvas。推荐两个基于html2canvas的类库。

  • html2pdf.js | Client-side HTML-to-PDF rendering using pure JS.

  • jsPDF – HTML5 PDF Generator | Parallax

以jsPDF为例:

jsPDF生成的pdf中文字会乱码,可能是因为字体的原因。

(更多文件类型,进一步补充…)




示例代码


const $ = document.querySelector.bind(document)
function downFile(blob, fileName) { const link = document.createElement('a') link.href = window.URL.createObjectURL(blob) link.download = fileName link.click() window.URL.revokeObjectURL(link.href)}
const txtBtn = $('#txt')
txtBtn.onclick = () => { let txtFile = new Blob(['Hello, world!'], { type: 'text/plain' }) downFile(txtFile, 'hello.txt')}

const htmlBtn = $("#html");
htmlBtn.onclick = () => { const html_string = `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1>码中人</h1> </body> </html>`; let htmlFile = new Blob([html_string], { type: 'text/html' }); downFile(htmlFile, "index.html")}

const cssBtn = $('#css');
cssBtn.onclick = () =>{ const css_string = `body { margin: 0; font-family: -apple-system,BlinkMacSystemFont,"Roboto",sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .editor-footer { position: absolute; bottom: 1.2em; right: 1.4em; width: 100%; } `; const cssFile = new Blob([css_string], { type: 'text/css' }); downFile(cssFile,"style.css")}
const jsonBtn = $("#json");
jsonBtn.onclick = () => { const debug = { hello: "blob" }; const blob = new Blob([JSON.stringify(debug, null, '\t')], { type: 'application/json' }); downFile(blob, "debug.json");}

const csvBtn = $('#csv');
csvBtn.onclick = () => { const csvString = ["a,b,c", "1,2,3", "一,二,三", "木,头,人"].join("\n"); const blob = new Blob(["\ufeff" + csvString], { type: 'text/csv;charset=UTF-8' }); downFile(blob, "test.csv")}

const imgBtn = $("#image");
imgBtn.onclick = () => { window.open('http://codeimg.mzh.ren/','codeimg')}

const pdfBtn = $('#pdf');
pdfBtn.onclick = () => { const { jsPDF } = window.jspdf const doc = new jsPDF() doc.text('click to www.mzh.ren!', 66, 88) const pdf_blob = new Blob([doc.output()], { type: 'application/pdf' }) downFile(pdf_blob,'jspdf.pdf');}


参考资料

  • Blob
  • Blob – Web API 接口参考 | MDN
  • MIME 类型 – HTTP | MDN
  • 常见 MIME 类型列表 – HTTP | MDN
  • 你不知道的 Blob-前端开发博客
  • UTF-8 csv fix for Excel | Camille Hodoul

往期推荐

图灵前端核心知识进阶系列(套装全10册)【涵盖前端入门、进阶必备知识!专家级前端工程师核心技能!】

代码之外的功夫 程序员精进之路

区块链基础知识25讲

学会这四句话,发家致富指日可待(读《胡雪岩》笔记)

理想与现实-软件工程师的一天

继续滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存