文章目录
- 学习链接
- Blob
- 创建
- 演示
- 分片
- 演示
- File
- input
- 手动拖拽
- fetch 从后端获取流
- 前端代码
- 后端代码
- window.showOpenFilePicker
- Filereader
- 示例1
- 示例2
- ArrayBuffer
- 创建buffer
- TypedArray读写buffer
- DataView读写buffer
- 与Blob对比
- Blob Url & DataUrl
- 示例1
- 示例2
学习链接
Blog & File B站视频
今天一次性给你讲清楚:File、Blob、FileReader、ArrayBuffer、Base64
web前端文件上传可选择的4种方式
Blob
Blob 全称为 binary large object
,即二进制大对象。blob对象本质上是js中的一个对象,里面可以储存大量的二进制编码格式的数据。Blob 对象一个不可修改,从Blob中读取内容的唯一方法是使用 FileReader。
创建
new Blob(array,options)
-
其有两个参数:
-
array:由 ArrayBuffer、ArrayBufferView、Blob、DOMString 等对象构成的,将会被放进 Blob;
-
options:它可能会指定如下两个属性
-
type:默认值为 “”,表示将会被放入到 blob 中的数组内容的 MIME 类型。
-
endings:默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入,不常用。
-
-
演示
这个 blob 对象上有两个属性:
- size:Blob对象中所包含数据的大小(字节);
- type:字符串,认为该Blob对象所包含的 MIME 类型。如果类型未知,则为空字符串。
分片
Blob 对象内置了 slice() 方法用来将 blob 对象分片
(注意:Blob对象是不可直接修改的,所以分片返回的是一个新的Blob对象
)
-
其有三个参数:
-
start:设置切片的起点,即切片开始位置。默认值为 0,这意味着切片应该从第一个字节开始;
-
end:设置切片的结束点,会对该位置之前的数据进行切片。默认值为blob.size;
-
contentType:设置新 blob 的 MIME 类型。如果省略 type,则默认为 blob 的原始值。
-
演示
File
File 接口基于 Blob,继承了 blob 的功能并将其扩展以支持用户系统上的文件。File 对象是特殊类型的 Blob,比Blob要多一些文件相关的属性。
File() 构造器创建新的 File 对象实例:var myFile = new File(bits, name[, options]);
- bits:一个包含ArrayBuffer,ArrayBufferView,Blob,或者 DOMString 对象的 Array — 或者任何这些对象的组合(所以可以这样创建File,new File([blob],‘test.zip’))。
- name:表示文件名称,或者文件路径。
- options: 可选,选项对象,包含文件的可选属性。可用的选项如下:
- type: DOMString,表示将要放到文件中的内容的 MIME 类型。默认值为 “” 。
- lastModified: 数值,表示文件最后修改时间的 Unix 时间戳(毫秒)。默认值为 Date.now()。
在 JavaScript 中,主要有两种方法来获取 File 对象:
-
<input> 元素上选择文件后返回的 FileList 对象;
-
文件拖放操作生成的 DataTransfer 对象;
-
window.showOpenFilePicker
-
fetch 从后端获取
input
- 但是,注意一下,每次change的时候,需要把这个input的value置为空,接着,仍然选择同一个文件时,也会触发这个change事件
- 可以添加hidden属性,让这个input标签隐藏掉。或者,写css样式让它隐藏。然后,手动用js调用这个input的click事件。
手动拖拽
<!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>
<style>
.box {
width: 300px;
height: 300px;
border: 1px solid red;
}
</style>
<script>
window.onload = () => {
let box = document.querySelector('.box')
box.ondragover = (e) => {
e.preventDefault();
}
box.ondrop = (e) => {
e.preventDefault();
let files = e.dataTransfer.files
console.log(files);
}
}
</script>
</head>
<body>
<div class="box"></div>
</body>
</html>
fetch 从后端获取流
从这里,我们可以看到都可以从后端拿到Blob对象了,那就可以操作这个Blob了,前端就可以创建个a标签,添加上download属性,然后用js模拟点击这个a标签,然后把这个a标签移除掉。
前端代码
<!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>
<style>
.box {
width: 300px;
height: 300px;
border: 1px solid red;
}
</style>
<script>
window.onload = () => {
let btn = document.querySelector('#btn')
btn.onclick = () => {
fetch('http://127.0.0.1:8083/file/downloadFile?filename=avatar3.png')
.then(response => {
console.log(response);
return response.blob()// 注意,只能读取一次
}).then(result=>{
console.log(result);
})
}
btn2.onclick = () => {
fetch('http://127.0.0.1:8083/test.json')
.then(response => {
console.log(response);
return response.json() // 注意,只能读取一次
}).then(result=>{
console.log(result);
})
}
}
</script>
</head>
<body>
<button id="btn">获取avatar3.png</button>
<button id="btn2">获取test.json</button>
</body>
</html>
后端代码
@GetMapping("downloadFile")
public void downloadFile(@RequestParam("filename") String filename) throws Exception {
// 告知浏览器这是一个字节流,浏览器处理字节流的默认方式就是下载
// 意思是未知的应用程序文件,浏览器一般不会自动执行或询问执行。浏览器会像对待,
// 设置了HTTP头Content-Disposition值为attachment的文件一样来对待这类文件,即浏览器会触发下载行为
response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
// ,该响应头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者网页的一部分),还是以附件的形式下载并保存到本地。
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,"attachment;fileName="+ URLEncoder.encode(filename, "UTF-8"));
File file = new File("d:/Projects/practice/test-springboot/src/main/resources/file/" + filename);
ServletOutputStream ros = response.getOutputStream();
FileInputStream fis = new FileInputStream(file);
byte[] bytes = new byte[2 * 1024];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
ros.write(bytes, 0, len);
}
ros.flush();
ros.close();
}
@GetMapping("test.json")
public Object test2() {
HashMap<String, Object> data = new HashMap<>();
data.put("halo", "world");
return data;
}
window.showOpenFilePicker
<!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>
<script>
window.onload = () => {
document.getElementById('openImageFile').addEventListener('click', async () => {
const handle = await showOpenFilePicker({
multiple: true,
types: [{
description: '图片',
accept: { 'image/png': ['.jpg', '.png'] }
}]
})
if (handle && handle.length) {
const handleFile = handle[0]
const file = await handleFile.getFile()
console.log(file);
}
}, false)
}
</script>
</head>
<body>
<button id="openImageFile">打开图片</button>
</body>
</html>
Filereader
通过上面我都知道了blob是不可修改也是无法读取里面的内容的。无法读取里面的内容肯定是不可行的。所以Filereader就提供了读取blob里面内容的方法
FileReader对象提供了以下方法来加载文件:
- readAsArrayBuffer()∶读取指定Blob中的内容,完成之后,result属性中保存的将是被读取文件的ArrayBuffer数据对象;
- readAsBinaryString()∶读取指定Blob中的内容,完成之后,result属性中将包含所读取文件的原始二进制数据;
- readAsDataURL()︰读取指定Blob 中的内容,完成之后,result属性中将包含一个data: URL格式的 Base64字符串以表示所读取文件的内容。
- readAsText()︰读取指定Blob 中的内容,完成之后,result属性中将包含一个字符串以表示所读取的文件内容。
示例1
将文件读取为base64 的字符串
<!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>
<script>
window.onload = () => {
let iptFile = document.querySelector('#iptFile')
iptFile.onchange = (e) => {
console.log(e.target.files);
let file = iptFile.files[0]
let fileReader = new FileReader()
fileReader.readAsDataURL(file)
fileReader.onload = function(e) {
console.log(e.target.result);
}
}
}
</script>
</head>
<body>
<input type="file" id="iptFile" name="file"/>
</body>
</html>
示例2
将存入的字符串的blob对象,读出来里面的内容
ArrayBuffer
我们可以把它理解为特殊的数组,特殊在哪里呢?
- ArrayBuffer 本身就是一个黑盒,不能直接读写所存储的数据,需要借助以下视图对象来读写
- TypedArray只是一个概念,实际使用的是那9个对象
创建buffer
new ArrayBuffer(bytelength)
- 参数:它接受一个参数,即 bytelength,表示要创建数组缓冲区的大小(以字节为单位。)
TypedArray读写buffer
DataView读写buffer
与Blob对比
-
ArrayBuffer 与 Blob 有啥区别呢?根据 ArrayBuffer 和 Blob 的特性,Blob 作为一个整体文件,适合用于传输;当需要对二进制数据进行操作时(比如要修改某一段数据时),就可以使用 ArrayBuffer。
-
通过ArrayBuffer创建Blob,然后通过FileReader读取里面的内容
Blob Url & DataUrl
-
可以使用 File 或 Blob 生成对应的 Url(除了使用现成的file对象,我们也可以用上面的方法,自己创建File对象,然后传进去。)
- URL.createObjectURL(blob) ; (blob url)
- FileReader.readAsDataURL(file); (data url)
-
生成的Url在一些a标签、img标签、iframe标签中可以使用。
示例1
<!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>
<script>
window.onload = () => {
let iptFile = document.querySelector('#iptFile')
let a = document.querySelector('#a')
let img = document.querySelector('#img')
iptFile.onchange = (e) => {
console.log(e.target.files);
let file = iptFile.files[0]
let blobUrl = URL.createObjectURL(file) ;
console.log(blobUrl);
a.href = blobUrl
img.src = blobUrl
}
}
</script>
</head>
<body>
<input type="file" id="iptFile" name="file"/>
<a href="#" id="a" download>link</a> <!-- 设置target='_blank'可以打开一个新页面;
加上download属性后,点击a标签可以下载,
不加download的话,如果浏览器支持查看该文件,将会预览这个文件,
如果不支持查看,将会下载这个文件;
可以使用js模拟点击a标签,来触发下载 -->
<img src="" id="img" alt="err" style="width:50px;height:50px;">
</body>
</html>
示例2
与上面的效果一致,只不过换成了FileReader
<!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>
<script>
window.onload = () => {
let iptFile = document.querySelector('#iptFile')
let a = document.querySelector('#a')
let img = document.querySelector('#img')
iptFile.onchange = (e) => {
console.log(e.target.files);
let file = iptFile.files[0]
let fileReader = new FileReader()
fileReader.onload = (e) => {
let dataUrl = fileReader.result
a.href = dataUrl
img.src = dataUrl
}
fileReader.readAsDataURL(file)
}
}
</script>
</head>
<body>
<input type="file" id="iptFile" name="file"/>
<a href="#" id="a" download>link</a> <!-- 设置target='_blank'可以打开一个新页面; 加上download属性后,点击a标签可以下载 -->
<img src="" id="img" alt="err" style="width:50px;height:50px;">
</body>
</html>