需求
今天来看一个挺常见的需求,上传图片时页面展示选择的图片,具体实现效果可以点击下面的「上传图片」按钮试试👇~(只能选择 .png 或 .jpeg 格式图片)
按钮设计源自 CodePen。
实现
HTML
首先是要使用的标签,上传文件需要用到 <input>
标签,并且指定属性 type
的值为 file
。
既然是预览图片,那么还需要指定选择文件的类型,使用 accept
属性可以规定期望的文件类型,为啥是「期望」呢?因为这只表示触发控件后默认允许选中的文件类型,比如在 MacOS 上,刁钻的用户仍可以在唤出的选择文件窗口中通过选项
➡️格式:所有文件
来选择其它类型的文件上传👿:
这就要求在 JavaScript 中还要判断用户选择的文件类型是否合法,后面会说到,先指定期望的类型为 PNG 或 JPG 格式的图片。并且,使用 <input>
标签时常用的做法是与 <label>
标签合用并且设置 <input>
为不可见,这样做的好处有两点:
<input>
的样式不易调整,而<label>
的样式可以为所欲为。<label>
增加了<input>
的命中区域。
所以现在的 HTML 是这样的:
|
|
CSS
样式上要做的一点是隐藏 <input>
,剩下的就完全自由化了:
|
|
另外,CodePen 上的各种按钮样式可真是美哭我了,比那些在线设计按钮样式的网站好太多了🤫,强烈安利。
JavaScript
到了功能实现的环节了,首先需要拿到选择图片的相关信息,在用户选择了文件时可以触发 <input>
的 change
事件,change
事件的参数内包含一个属性 files
,其对应的值 FileList
是一个类数组,由一个个 File
对象组成,对象内存储着用户选择的文件的信息,比如名字、大小、类型等。
因为
<input>
有一个multiple
属性,设置后可以选择多个文件,所以FileList
的类型为类数组。
能获取到文件的类型就可以做我们上面说的校验文件合法性了:
|
|
接下来需要在页面中显示图片,需要一个展示图片的容器,当然也可以使用 JavaScript 直接插入到页面,但前者更容易设置图片的样式。显示图片有两种方式——FileReader 和对象 URL。
1. FileReader
FileReader 对象用来异步读取文件的内容,可读取的目标正是获取到的 File 对象。
|
|
readAsDataURL()
方法可读取传入的 File 对象,读取完成后会触发 onload
事件,事件的参数中就包含图片的 base64 编码,然后将它设置为页面中用于展示图片的 <img>
标签的 src
属性,图片成功显示~
2. 对象 URL
对象 URL 指的是 window.URL.createObjectURL()
和 window.URL.revokeObjectURL()
方法,window.URL.createObjectURL()
方法同样可传入一个 File 对象,但是返回的并不是图片的 base64 编码,而是一个特殊的字符串[1],长这个亚子:
blob:null/1a92aff8-90a4-4421-966c-4b151078da0f
不管它叫啥,它只能由浏览器内部生成,将 <img>
标签的 src
属性设置成它也可以显示图片:
|
|
(URL || webkitURL).createObjectURL()
为兼容写法。
结语
效果就不用再展示了,完整方法和上面炫酷按钮的案例源码都在我的 GitHub 中。那么到底要选择使用哪个方式呢?
首先,这两个 API 的兼容性都是差不多的,IE 10+ 和所有主流浏览器都支持。从性能上来说,FileReader.readAsDataURL()
是异步执行,而 createObjectURL()
是同步执行,而 Blob URLs 占用的内存比 base64 更小,所以总体来讲 createObjectURL()
占优,但其关闭标签页时才会释放内存,使用的时候应考虑是否要用 window.URL.revokeObjectURL()
方法手动释放内存。
References & Resources
- 在 web 应用程序中使用文件 | MDN
- Preview an image before it is uploaded | Stack Overflow
- What is a blob URL and why it is used? | Stack Overflow
W3C 称它为 Blob URLs,MDN 称它为 Object-URLs。