目前網(wǎng)上有很多支持圖片上傳時(shí)進(jìn)行預(yù)覽的插件,功能完備航攒,界面優(yōu)雅,使用起來(lái)也很方便趴梢。一直以來(lái)也就只是用用漠畜,沒(méi)有想過(guò)這些插件背后的實(shí)現(xiàn)原理。趁著今天有點(diǎn)時(shí)間坞靶,也來(lái)學(xué)習(xí)學(xué)習(xí)憔狞。
[toc]
追根溯源
設(shè)想
一開始,按照我的思路彰阴,預(yù)覽可能是這么來(lái)實(shí)現(xiàn)的瘾敢。本地選中一張圖片,嵌入html的同時(shí)會(huì)顯示圖片的本地的絕對(duì)路徑尿这,然后通過(guò)js簡(jiǎn)單的進(jìn)行設(shè)置簇抵,應(yīng)該就可以實(shí)現(xiàn)預(yù)覽效果了。
但是實(shí)際上射众,目前只有低版本的IE瀏覽器才能實(shí)現(xiàn)這么個(gè)效果碟摆。究其原因是瀏覽器廠商為了進(jìn)一步強(qiáng)化安全,限制了file標(biāo)簽
直接讀取本地路徑的能力叨橱,在HTML5下只有通過(guò)FileReader的API來(lái)實(shí)現(xiàn)這一需求了典蜕。
比如對(duì)于CSDN寫博客的時(shí)候上傳一張圖片,得到的只會(huì)是一個(gè)fakepath
罗洗。有圖為證:
原理
FileReader就是html5為我們提供的讀取文件的api愉舔。它的作用就是把文本流按指定格式讀取到緩存,以供js調(diào)用伙菜。
FileReader有四種讀取文件的方式:
readAsBinaryString讀取為二進(jìn)制碼
readAsDataURL讀取為 DataURL
readAsText讀取為文本
readAsArrayBuffer
根據(jù)本次實(shí)現(xiàn)的目標(biāo)轩缤,使用第二種方式即可。img標(biāo)簽的src就是這個(gè)圖片的編碼后的DataURL
仇让。如圖所示:
DataURL
淺析
DataURL
說(shuō)來(lái)可是有很多內(nèi)容要研究的典奉,但是這次用的比較淺顯,就把基礎(chǔ)的了解下就行了丧叽。
格式
DataURL
有其固定的格式卫玖,如下:
data:[文件格式];base64,[文本流base64編碼]。
舉個(gè)例子:
-
jpg
格式:data:image/jpeg;base64,/9j/4...
-
png
格式:data:image/png;base64,iVBORw...
-
gif
格式:data:image/gif;base64,R0lGOD...
png
格式的圖片編碼信息
預(yù)覽實(shí)現(xiàn)
好了踊淳,弄明白了這些原理性的東西假瞬,就可以著手進(jìn)行實(shí)現(xiàn)了陕靠。
HTML
<form action="#" method="POST">
<legend>
圖片上傳
</legend>
<fieldset>
<input type="file" name="pic1" id="pic1" onchange="preview(this)" multiple="multiple"
accept="image/x-png, image/jpg, image/jpeg, image/gif">
<br><br>
</fieldset>
<input type="button" value="上傳">
</form>
<div id="container">
</div>
在代碼中使用了HTML5的一些新特性。用來(lái)過(guò)濾待上傳的圖片格式脱茉。
JavaScript控制
接下來(lái)就是預(yù)覽功能的實(shí)現(xiàn)了剪芥。目標(biāo)就是將圖片轉(zhuǎn)換成DataURL,然后對(duì)預(yù)覽區(qū)進(jìn)行子元素的添加操作琴许。
<script>
var msg = "您可以上傳png, jpg, 或者gif格式的圖片";
var filter = {
"jpeg": "/9j/4",
"gif": "R0lGOD",
"png": "iVBORw"
};
function preview(file) {
var container = document.getElementById("container");
container.innerHTML = "";
if (window.FileReader) {
for (var index=0, f; f = file.files[index]; index++) {
var filereader = new FileReader();
filereader.onload = function (event) {
var srcpath = event.target.result;
if (!validateImg(srcpath)) {
console.log("H5"+msg);
} else {
showPreviewImage(srcpath);
}
};
filereader.readAsDataURL(f);
}
} else {
if (!/\.jpg$|\.png$|\.gif$/i.test(file.value)) {
console.log("原生"+msg);
} else {
showPreviewImage(file.value);
}
}
}
function validateImg(data) {
console.log(data);
var pos = data.indexOf(",") + 1;
for (var e in filter) {
if (data.indexOf(filter[e]) === pos) {
return e;
}
}
return null;
}
function showPreviewImage(src) {
console.log(src);
var img = document.createElement('img');
img.src = src;
img.style = "width:64px;height:auto;"
container.appendChild(img);
}
</script>
預(yù)覽效果
總的來(lái)說(shuō)代碼就算是完成了税肪,接下來(lái)看下實(shí)現(xiàn)的效果。由于沒(méi)有設(shè)置樣式榜田,所以看起來(lái)很簡(jiǎn)陋益兄,有興趣的自己用樣式控制一下即可。
打包封裝
簡(jiǎn)易封裝
為了方便實(shí)用箭券,特使用原生JavaScript封裝了一個(gè)這樣的組件净捅。詳細(xì)代碼如下:
/**
* Created by biao on 2017/7/10.
* Description: A simple tool for previewing images for uploading.
* Blog: http://blog.csdn.net/marksinoberg
* GitHub: https://github.com/guoruibiao
*/
function ImgPrevirewer(config) {
/**
* The tag ID for upload images.
*/
this.fileId = config.fileId;
/**
* tip for error message.
* @type {string}
*/
this.tip = config.tip;
/**
* The ID for the container which contains img tags.
* @type {string}
*/
this.containerId = config.containerId;
/**
* CSS style for previewing imgs.
* @type {string}
*/
this.imgStyle = config.imgStyle;
/**
* 過(guò)濾圖片格式,可進(jìn)行相對(duì)應(yīng)的刪減操作辩块。
* @type {{jpeg: string, gif: string, png: string}}
*/
this.filter = {
/**
* jpg或者jpeg格式的圖片蛔六。
*/
"jpeg": "/9j/4",
/**
* gif格式的圖片。
*/
"gif": "R0lGOD",
/**
* PNG格式的圖片废亭。
*/
"png": "iVBORw"
};
/**
* 開始預(yù)覽国章。自動(dòng)調(diào)用原生JavaScript實(shí)現(xiàn)相關(guān)元素的定位以及渲染。
*/
this.preview = function () {
var file = document.getElementById(this.fileId);
var container = document.getElementById(this.containerId);
container.innerHTML = "";
/**
* 防止內(nèi)部作用域覆蓋問(wèn)題滔以。
* @type {ImgPrevirewer}
*/
var that = this;
// HTML5 需要使用FileReader的相關(guān)API來(lái)讀取本地?cái)?shù)據(jù)捉腥。
if (window.FileReader) {
// 針對(duì)多個(gè)上傳文件批量處理。
for (var index = 0, f; f = file.files[index]; index++) {
var filereader = new FileReader();
filereader.onload = function (event) {
var srcpath = event.target.result;
if (!that.validateImg(srcpath)) {
console.log(this.tip);
} else {
that.showPreviewImg(srcpath);
}
};
filereader.readAsDataURL(f);
}
} else {
// 低版本降級(jí)處理你画。
if (!/\.jpg$|\.png$|\.gif$/i.test(file.value)) {
console.log(this.tip);
} else {
that.showPreviewImg(file.value);
}
}
}
/**
* 根據(jù)圖片的base64編碼格式查看圖片是否符合要求抵碟。
* @param data 編碼后的圖片數(shù)據(jù)。
* @returns {*}
*/
this.validateImg = function (data) {
var pos = data.indexOf(",") + 1;
for (var e in this.filter) {
if (data.indexOf(this.filter[e]) === pos) {
return e;
}
}
return null;
}
/**
* 開始實(shí)現(xiàn)對(duì)圖片的預(yù)覽坏匪,根據(jù)this.imgStyle進(jìn)行相關(guān)渲染操作拟逮。
* @param src
*/
this.showPreviewImg = function (src) {
var img = document.createElement('img');
img.src = src;
img.style = this.imgStyle;
container.appendChild(img);
}
}
使用方式
下面來(lái)一個(gè)簡(jiǎn)單的“模板式”使用技巧。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
<script src="img-previewer.js"></script>
</head>
<body>
<input type="file" id="file" multiple onchange="preview()">
<div id="container">
</div>
<script>
function preview(){
var config = {
tip: "請(qǐng)上傳格式為png, gif或者jpg的圖片",
fileId: "file",
containerId: "container",
imgStyle: "width:320px;height:auto;border-radius:64%;"
}
var previewer = new ImgPrevirewer(config);
previewer.preview();
}
</script>
</body>
</html>
測(cè)試
為了保證這個(gè)組件的穩(wěn)定性适滓,接下來(lái)來(lái)個(gè)簡(jiǎn)單的測(cè)試敦迄。
- 首先是在Chrome瀏覽器上,發(fā)現(xiàn)可以正常工作凭迹。
-
接下來(lái)是在Edge瀏覽器上的測(cè)試罚屋。(發(fā)現(xiàn)樣式不兼容)
Edge瀏覽器功能實(shí)現(xiàn)了,但是樣式不夠兼容 -
不出所料嗅绸,IE系的瀏覽器樣式都沒(méi)能兼容脾猛。
IE瀏覽器樣式不兼容
。鱼鸠。猛拴。
最終發(fā)現(xiàn)羹铅,Chrome等WebKit內(nèi)核的瀏覽器可以完美支持,對(duì)于微軟系瀏覽器而言愉昆,功能可以滿足职员,但是樣式上不兼容,這點(diǎn)可以通過(guò)特定的瀏覽器頭來(lái)實(shí)現(xiàn)跛溉,不再過(guò)多敘述焊切。
總結(jié)
總的來(lái)說(shuō),關(guān)于圖片上傳時(shí)的預(yù)覽功能芳室,實(shí)用性還是很強(qiáng)的蛛蒙。對(duì)于一個(gè)網(wǎng)站可以算是一個(gè)加分項(xiàng)。當(dāng)然了渤愁,該網(wǎng)站有一個(gè)設(shè)計(jì)感不錯(cuò)的美工或者前端,不像我做出的頁(yè)面好難看(⊙﹏⊙)b深夯。
大概就是這樣咯抖格,有需要的盡管拿去使用。