简单的下载示例:
<a href="url"></a>
<img src="url" />
文件下载原理:
java后台只能做到返回二进制流或文件给前端,最终在前端页面创建一个a,然后触发a的点击事件实现点击下载效果。
接口:
export function exportfile_goods(jsonobj){ // 导出商品文件
return fetch({
url:'/api/goods/manager/product/export',
method: 'post',
params:jsonobj,
responseType: 'arraybuffer',
});
}
调用接口:
exportfile_goods(params).then(res => {
if(res.data){ // res.data为后台返回的数据
const filename = decodeURI(res.headers['content-disposition'].split(';')[1]).replace('filename=',''); //先解码,得文件名
let blob = new Blob([res.data],{type: "application/vnd.ms-excel"}); //指定文件MIME
let a = document.createElement('a');
a.download = filename; //指定下载的文件名
a.href = URL.createObjectURL(blob); // URL对象
a.click(); // 模拟点击
URL.revokeObjectURL(a.href); // 释放URL 对象
// document.body.removeChild(a);//dom元素找不到,报错
}
后台返回的乱码文件流:
请求方式:post/get 都可以
各知识点讲解:
1.decodeURI解码
encodeURI 编码,对以下统一资源标识符的处理
uriReserved ::: one of ; / ? : @ & = + $ ,
uriMark ::: one of - _ . ! ~ * ' ( )
注: 以上语法是基于 RFC 2396 (请求注解--Request For Comments)的,并且较新的 RFC 3986 引入的更改没有反应在这里。
当 URI 里包含一个没在上面列出的字符或有时不想让给定的保留字符有特殊意义,那么必须编码这个字符。字符被转换成 UTF-8 编码,首先从 UTF-16 转换成相应的代码点值的替代。(注:对于 [0,127] 范围的代码单元,转换到单字节时它们的值是相同的。)然后返回的字节序列转换为一个字符串,每个字节用一个“%xx”形式的转移序列表示。
描述编码和转义过程的抽象操作 Encode 需要两个字符串参数 string 和 unescapedSet。
- 令 strLen 为 string 的字符个数。
- 令 R 为空字符串。
- 令 k 为 0。
- 重复
- 如果 k 等于 strLen,返回 R。
- 令 C 为 string 中位置为 k 的字符。
- 如果 C 在 unescapedSet 里,则
- 令 S 为一个只包含字符 C 的字符串。
- 令 R 为之前 R 的值和 S 连接得到的一个新字符串值。
- 否则,C 不在 unescapedSet 里
- 如果 C 的代码单元值不小于 0xDC00 并且不大于 0xDFFF,则抛出一个 URIError 异常。
- 如果 C 的代码单元值小于 0xD800 或大于 0xDBFF,则
- 令 V 为 C 的代码单元值。
- 否则
- k 递增 1。
- 如果 k 等于 strLen,抛出一个 URIError 异常。
- 令 kChar 为 string 的 k 位置的字符的代码单元值。
- 如果 kChar 小于 0xDC00 或大于 0xDFFF,则抛出一个 URIError 异常。
- 令 V 为 (((C的代码单元值 ) – 0xD800) * 0x400 + (kChar – 0xDC00) + 0x10000)。
- 令 Octets 为 V 执行 UTF-8 转换的结果字节数组,令 L 为这个字节数组的长度。
- 令 j 为 0。
- 只要 j < L,就重复
- 令 jOctet 为 Octets 的 j 位置的值。
- 令 S 为一个包含三个字符“%XY”的字符串,这里 XY 是编码 jOctet 值的两个大写16进制数字。
- 令 R 为之前 R 的值和 S 连接得到的一个新字符串值。
- j 递增 1。
- 如果 C 的代码单元值不小于 0xDC00 并且不大于 0xDFFF,则抛出一个 URIError 异常。
- k 递增 1。
描述反转义和解码过程的抽象操作 Decode 需要两个字符串参数 string 和 reservedSet。
- 令 strLen 为 string 的字符个数。
- 令 R 为空字符串。
- 令 k 为 0。
- 重复
- 如果 k 等于 strLen,返回 R。
- 令 C 为 string 的 k 位置的字符。
- 如果 C 不是‘%’,则
- 令 S 为只包含字符 C 的字符串。
- 否则,C 是‘%’
- 令 start 为 k。
- 如果 k + 2 大于或等于 strLen,抛出一个 URIError 异常。
- 如果 string 的 (k + 1) 和 (k + 2) 位置的字符没有表示为16进制数字,则抛出一个 URIError 异常。
- 令 B 为 (k + 1) 和 (k + 2) 位置的两个16进制数字表示的8位值。
- k 递增 2.
- 如果 B 的最高有效位是 0,则
- 令 C 为代码单元值是 B 的字符。
- 如果 C 不在 reservedSet 里,则
- 令 S 为只包含字符 C 的字符串。
- 否则,C 在 reservedSet 里
- 令 S 为 string 的从位置 start 到位置 k 的子字符串。
- 否则,B 的最高有效位是 1
- 令 n 为满足 (B << n) & 0x80 等于 0 的最小非负数。
- 如果 n 等于 1 或 n 大于 4,抛出一个 URIError 异常。
- 令 Octets 为一个长度为 n 的字节数组。
- 将 B 放到 Octets 的 0 位置。
- 如果 k + (3 * (n – 1)) 大于或等于 strLen,抛出一个 URIError 异常。
- 令 j 为 1。
- 重复,直到 j < n
- k 递增 1。
- 如果 string 的 k 位置的字符不是‘%’,抛出一个 URIError 异常。
- 如果 string 的 (k + 1) 和 (k + 2) 位置的字符没有表示为16进制数字,抛出一个 URIError 异常。
- 令 B 为 string 的 (k + 1') 和 (k + 2) 位置的两个16进制数字表示的8位值。
- 如果 B 的两个最高有效位不是二进制的 10,抛出一个 URIError 异常。
- k 递增 2。
- 将 B 放到 Octets 的 j 位置。
- j 递增 1。
- 令 V 为给 Octets 执行 UTF-8 转换得到的值,这是从一个字节数组到一个21位值的转换过程。
如果 Octets 不包含有效的 UTF-8 编码的 Unicode 代码点,则抛出一个 URIError 异常。
- 如果 V 小于 0x10000,则
- 令 C 为代码单元值是 V 的字符。
- 如果 C 不在 reservedSet 里,则
- 令 S 为只包含字符 C 的字符串。
- 否则,C 在 reservedSet 里
- 令 S 为 string 的从位置 start 到位置 k 的子字符串。
- 否则,V ≥ 0x10000
- 令 L 为 (((V – 0x10000) & 0x3FF) + 0xDC00)。
- 令 H 为 ((((V – 0x10000) >> 10) & 0x3FF) + 0xD800)。
- 令 S 为代码单元值是 H 和 L 的两个字符组成的字符串。
- 令 n 为满足 (B << n) & 0x80 等于 0 的最小非负数。
- 令 R 为之前的 R 和 S 连接成的新字符串。
- k 递增 1。
注: 统一资源标识符的语法由 RFC 2396 给出,这里并没有反应更新的替换了 RFC 2396 的 RFC 3986。RFC 3629 给出了实现 UTF-8 的正式描述。
在 UTF-8 中,用 1 到 6 个位的字节序列来编码字符。只有“序列”中高阶位设置为 0 的字节,其余的 7 位才用于编码字符值。在一个 n 个字节的序列中,n > 1,初始字节有 n 个设置为 1 的高阶位,其后的一位设置为 0。这个字节的其它位包含了字符编码的位。随后的其它字节都是最高位为 1、次高位为 0、剩下的 6 位为字符编码。表21 指定了 ECMAScript 字符可能的 UTF-8 编码。
单位代码值 | 表现 | 第一字节 | 第二字节 | 第三字节 | 第四字节 |
---|---|---|---|---|---|
0x0000 - 0x007F | 00000000 0zzzzzzz | 0zzzzzzz | |||
0x0080 - 0x07FF | 00000yyy yyzzzzzz | 110yyyyy | 10zzzzzz | ||
0x0800 - 0xD7FF | xxxxyyyy yyzzzzzz | 1110xxxx | 10yyyyyy | 10zzzzzz | |
0xD800 - 0xDBFF 挨着 0xDC00 - 0xDFFF | 110110vv vvwwwwxx 挨着 110111yy yyzzzzzz | 11110uuu | 10uuwwww | 10xxyyyy | 10zzzzzz |
0xD800 - 0xDBFF 不挨着 0xDC00 - 0xDFFF | 引发URIError | ||||
0xDC00 - 0xDFFF | 引发URIError | ||||
0xE000 - 0xFFFF | xxxxyyyy yyzzzzzz | 1110xxxx | 10yyyyyy | 10zzzzzz |
在这里
uuuuu = vvvv + 1
以补足替代符的 0x10000 附加值,在 Unicode 标准 3.7 章节。
0xD800-0xDFFF 范围的代码单元值用来编码替代符对;如上将 UTF-16 替代符对转换组合成一个 UTF-32 表示法,并将其编码到一个 UTF-8 的21位值中。这就是替代符对的解码方式。
RFC 3629 禁止对无效 UTF-8 字节序列的解码。例如,无效序列 C0 80 不能解码成字符 U+0000。当 Decode 算法的实现遇到这样的无效序列必须抛出一个 URIError 异常。
decodeURI (encodedURI)
decodeURI 函数计算出一个新版 URI,将 URI 中可能是 encodeURI 函数引入的每个转义序列和 UTF-8 编码组替换为代表它们的字符。不是 encodeURI 导入的转义序列不会被替换。
当以一个参数 encodedURI 调用 decodeURI 函数,采用如下步骤:
- 令 uriString 为 ToString(encodedURI)。
- 令 reservedURISet 为一个包含 uriReserved 中的所有字符组成的字符串连接上 "#" 组成的字符串。
- 返回调用 Decode(uriString, reservedURISet) 的结果。
注: "#" 字符不会从转义序列中解码,即使它不是 URI 保留字符。
decodeURIComponent (encodedURIComponent)
decodeURIComponent 函数计算出一个新版 URI,将 URI 中可能是 encodeURIComponent 函数引入的每个转义序列和 UTF-8 编码组替换为代表它们的字符。
当以一个参数 encodedURIComponent 调用 decodeURIComponent 函数,采用如下步骤:
- 令 componentString 为 ToString(encodedURIComponent)。
- 令 reservedURIComponentSet 为一个空字符串。
- 返回调用 Decode(componentString, reservedURIComponentSet) 的结果。
encodeURI (uri)
encodeURI 函数计算出一个新版 URI,将 URI 中某些字符的每个实例替换为代表这些字符 UTF-8 编码的一个,两个或三个转义序列。
当以一个参数 uri 调用 encodeURI 函数,采用如下步骤:
- 令 uriString 为 ToString(uri)。
- 令 unescapedURISet 为一个包含 uriReserved 和 uriUnescaped 中所有字符组成的字符串连接上 "#" 组成的字符串。
- 返回调用 Encode(uriString, unescapedURISet) 的结果。
注: 字符 "#" 不会被编码为一个转义序列,即使它不是 URI 保留字符或非转义字符。
encodeURIComponent (uriComponent)
encodeURIComponent 函数计算出一个新版 URI,将 URI 中某些字符的每个实例替换为代表这些字符 UTF-8 编码的一个,两个或三个转义序列。
当以一个参数 uriComponent 调用 encodeURIComponent 函数,采用如下步骤:
- 令 componentString 为 ToString(uriComponent)。
- 令 unescapedURIComponentSet 为一个包含 uriUnescaped 中所有字符组成的字符串。
- 返回调用 Encode(componentString, unescapedURIComponentSet) 的结果。
2. 新方法创建Blob 对象
blob长这样:
1. 使用旧方法创建 Blob 对象。
旧的方法使用 BlobBuilder 来创建一个Blob 实例,并且使用一个 append() 方法,将字符串(或者 ArrayBuffer 或者 Blob,此处用 string 举例)插入,一旦数据插入成功,就可以使用 getBlob() 方法设置一个 mime 。
<script>
var builder = new BolbBuilder();
builder.append("Hello World!");
var blob = builder.getBlob("text/plain");
2.在新的方法中直接可以通过 Blob() 的构造函数来创建了。
构造函数,接受两个参数,第一个为一个数据序列,可以是任意格式的值,例如,任意数量的字符串,Blobs 以及 ArrayBuffers。第二个参数,是一个包含了两个属性的对象,其两个属性分别是:
type -- MIME 的类型。
endings -- 决定 append() 的数据格式,(数据中的 \n 如何被转换)可以取值为 "transparent" 或者 "native"(t* 的话不变,n* 的话按操作系统转换;t* 为默认) 。
var blob = new Blob(["Hello World!"],{type:"text/plain"});
3.URL对象这是一个实验中的功能
此功能某些浏览器尚在开发中,请参考浏览器兼容性表格以得到在不同浏览器中适合使用的前缀。由于该功能对应的标准文档可能被重新修订,所以在未来版本的浏览器中该功能的语法和行为可能随之改变。
URL.createObjectURL() 静态方法会创建一个 DOMString
,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document
绑定。这个新的URL 对象表示指定的 File
对象或 Blob
对象。
提示:使用 一个MediaStream
对象作为此方法的输入正在被弃用。这个方法正在被讨论是否应该被移除. 所以,你应当在你使用MediaStream
时避免使用这个方法,而用HTMLMediaElement.srcObject()
替代.
Note: 此特性在 Web Worker 中可用。
语法节
objectURL = URL.createObjectURL(blob);
参数节
blob
是用来创建 URL 的 File
对象或者 Blob
对象
示例节
查看使用对象URL显示图片.
附注节
在每次调用 createObjectURL()
方法时,都会创建一个新的 URL 对象,即使你已经用相同的对象作为参数创建过。当不再需要这些 URL 对象时,每个对象必须通过调用 URL.revokeObjectURL()
方法来释放。浏览器会在文档退出的时候自动释放它们,但是为了获得最佳性能和内存使用状况,你应该在安全的时机主动释放掉它们。
规范节
规范 | 状态 | 备注 |
---|---|---|
File API URL | Working Draft | Initial definition. |
浏览器兼容性节
We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!
- Desktop
- Mobile
特性 | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari (WebKit) |
---|---|---|---|---|---|
基本支持 | 8 [1] 23 | 4.0 (2) | 10 | 15 | 6 [1] 7 |
In a Web Worker | 10 [1] 23 | 21 (21) | 11 | 15 | 6 [1] 7 |