前言

本文记录 JavaScript 中的文件流,持续更新中……


使用Blob下载文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 模拟下载请求
function download() {
axios({
url: `http://localhost:2000/download`,
method: 'GET',
responseType: 'blob',
}).then(result => {
// 如果接口请求填写了 responseType: 'blob',可以不需要new blob
const blob = new Blob([result.data], {
type: "image/jpeg"
})
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.download = `1.png`;
a.href = url;
a.click()

}).catch((err) => {
console.log(err);
})
}

// 下载函数
export const downloadFile = async (params, fileName) => {
// 我们使用axios设置接口返回类型 responseType: "blob", 所以这里从后端返回的是blob。
const results = await download(params);

const a = document.createElement("a");
a.download = fileName + ".xlsx";
// 生成blob url。这里可以使用Blob对象或者File对象
a.href = window.URL.createObjectURL(results);
a.style.display = "none";
document.body.appendChild(a);
a.click();
// 释放内存
window.URL.revokeObjectURL(a.href);
document.body.removeChild(a);
};

File

参考:MDN文档 重学JS | 玩转File API (掘金)

FileReader 对象允许 Web 应用程序异步读取存储在用户计算机上的文件,支持读取 File 或 Blob 对象指定要读取的文件或数据,是一种异步文件读取机制。

FileReader 常用 api

方法

  1. readAsText(file,encoding):以纯文本的形式读取文件
  2. readAsDataURL(file):读取文件并将文件以数据URI的格式保存到result
  3. readAsBinaryString(file):读取文件并将一个字符串保存在result属性中,字符串中的每个字符表示一字节
  4. readAsArrayBuffer(file):读取文件并将一个包含文件内容的ArrayBuffer保存到result属性中

事件

  1. progress:表示又读取了数据,50ms左右触发一次
  2. error:发生了错误触发
  3. load:已经读完了整个文件触发
  4. abort:中断读取过程
  5. loadend:整个过程结束,load、error、abort事件触发后触发的事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<input type="file">
<script>
let fileReader = new FileReader() // 创建一个 fileReader 对象
let file = document.querySelector('input') // 获取节点
file.onchange = function() {
let fileType = this.files[0]; // 获取上传的文件
fileReader.readAsDataURL(this.files[0]); // base64 方式读取
fileReader.onload = (e) => { // 读取操作成功完成时调用
const result = e.target.result
console.log(result) // 上传文件转的地址
}

}

</script>


// ----------------------
// 其他补充
<body>
<input type='file' onchange="fileChange(this)" />
<div id='output' />
<script>
function fileChange(target){
// 得到第一个上传的文件对象
var file = target.files[0],
output = document.getElementById('output'),
type = '',
reader = new FileReader();

if(/image/.test(file.type)){
type = 'image'
reader.readerAsDataURL(file)
}else{
type = 'text'
reader.readAsText(file)
}

// 文件读取进度
reader.onprogress = function(e){
if(e.lengthComputable){
console.log('当前读取进度为':e.loaded+'/'+e.total)
}
}
// 文件读取错误
reader.onerror = function(){
// 1: 未找到文件 2:安全性错误 3:读取中断 4:文件不可读 5:编码错误
var errCode = reader.error.code
}
// 文件读取完成
reader.onload = function(){
// reader.result: 读取的文件
var html = type==='image'?`<img src='${reader.result}'>`:reader.result
output.innerHtml = html
}
// 整个流程结束
reader.onloadend = function(){}
}
</script>
</body>

URL.createObjectURL()方法

接收一个参数表示指定的 File 对象或 Blob 对象。

1
2
3
4
5
6
7
8
var file = document.getElementById("inp");
file.onchange = function () {
let url = window.URL.createObjectURL(this.files[0])
console.log(url)
}

// 移除 url
window.URL.revokeObjectURL(url)

文件上传

单张图片上传

1
2
3
4
5
6
// 一般隐藏 input 自定义样式,其他div点击调用 input.click() 即可
// <input type="file" accept="image/*"> 表示上传任意图片格式
// 1.使用 FORM-DATA
let formData = new FormData();
formData.append('file', file); // 此处的 file 是input中拿到的
// 2.使用 fileReader 的 url (比较少用)

一次多张图片上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<body>
<section class="uploadBox clearfix">
<div class="card button">
<input type="file" id="uploadInp" accept="image/*" multiple>
</div>
<!-- <div class="card">
<img src="images/1.png" alt="">
<div class="progress">
<div class="line"></div>
</div>
<div class="mark"></div>
</div> -->
</section>

<script>
(function () {
//请求封装
function postRequest(url, data, config) {
config = config || {};
return axios.post(`xxx${url}`, data, config).then(response => {
return response.data;
});
}

//文件读取
function fileReader(file) {
return new Promise(resolve => {
let reader = new FileReader;
reader.readAsDataURL(file);
reader.onload = ev => {
resolve(ev.target.result);
};
});
}
// 获取 DOM
let uploadBox = document.querySelector('.uploadBox'),
button = uploadBox.querySelector('.button'),
uploadInp = uploadBox.querySelector('#uploadInp');
// 调用input上传文件
button.onclick = function () {
uploadInp.click();
};
uploadInp.onchange = async function () {
let self = this,
files = Array.from(self.files); // 获取上传文件列表,伪数组转化为数据
if (files.length === 0) return;

// 构建上传列表
let uploadList = [];
files.forEach((file, index) => {
uploadList[index] = {
file: file,
base64: null,
card: null
};
});

// BASE64 和动态创建 Card
let base64List = await Promise.all(files.map(file => fileReader(file))),
frag = document.createDocumentFragment(); // 创建文档碎片
base64List.forEach((base64, index) => {
let card = document.createElement('div');
card.className = 'card';
card.innerHTML = `
<img src="${base64}" alt="">
<div class="progress">
<div class="line"></div>
</div>
<div class="mark"></div>
`;
frag.appendChild(card); // 防止多次真实DOM 插入 card
//完善上传列表
uploadList[index].base64 = base64;
uploadList[index].card = card;
});
uploadBox.appendChild(frag);


// 按照上传列表,批量上传图片 && 监听进度
uploadList.forEach(async item => {
let {
file,
base64,
card
} = item;

let data = {
chunk: encodeURIComponent(base64),
filename: file.name
},
config = {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
// 上传进度检测
onUploadProgress(ev) {
// ev.loaded && ev.total
let ratio = ev.loaded / ev.total * 100 + '%';
card.querySelector('.line').style.width = ratio;
}
};
let response = await postRequest('/upload', Qs.stringify(data), config);
if (response.code === 0) {
// 上传成功
await delay();
let progress = card.querySelector('.progress'),
mark = card.querySelector('.mark');
card.removeChild(progress);
card.removeChild(mark);
}
});
};
})();
</script>
</body>

拖拽上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// <div class="uploadBox" contenteditable></div>
// 必须添加 contenteditable 可编辑属性
let uploadBox = document.querySelector('.uploadBox');
uploadBox.ondrop = function (ev) {
ev.preventDefault();
// 获取拖拽放置到盒子中的文件
console.log(ev.dataTransfer.files[0]);
};

// 其他事件
uploadBox.ondragenter = function () {
uploadBox.innerHTML = '请释放鼠标';
};
let num = 0
uploadBox.ondragover = function () {
uploadBox.innerHTML = '一直触发hover' + ++num;
};
uploadBox.ondragleave = function () {
uploadBox.innerHTML = '请将文件拖拽到此区域';
};

补充

参考:掘金

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1.字符编码
// 两者区别为:encodeURI的编码范围没有小一点(有点符号不会被编码)
let str = 'abc/:'
encodeURI(str); // 编码成 abc/:
decodeURI() // 解码
encodeURIComponent(str); // abc%2F%3A
decodeURIComponent()

// 位、字节、字的概念
// 位表示计算机中的0和1、一字节为8位、字可能为2字节或者4字节、8字节

// ASCII、unicode、GB2312/GBK都是是字符集,定义每个字符对应的数字。
// UTF-8、UTF-16等是unicode字符集的编码格式,定义“字符对应的数字”如何以二进制的方式存储。


// js 中的 File类
let file = new File(array, name,options)
// array:由ArrayBuffer, ArrayBufferView, Blob, DOMString 等对象构成,
// 如果是数组每一个元素对应着文件中一行的内容
// name: 文件名
// options:设置一些属性,type属性、lastModified