js實現圖片上傳預覽原理分析
目前網上有很多支持圖片上傳時進行預覽的插件,功能完備,界面優(yōu)雅,使用起來也很方便。一直以來也就只是用用,沒有想過這些插件背后的實現原理。趁著今天有點時間,也來學習學習。
追根溯源
設想
一開始,按照我的思路,預覽可能是這么來實現的。本地選中一張圖片,嵌入html的同時會顯示圖片的本地的絕對路徑,然后通過js簡單的進行設置,應該就可以實現預覽效果了。
但是實際上,目前只有低版本的IE瀏覽器才能實現這么個效果。究其原因是瀏覽器廠商為了進一步強化安全,限制了file標簽直接讀取本地路徑的能力,在HTML5下只有通過FileReader的API來實現這一需求了。
比如對于CSDN寫博客的時候上傳一張圖片,得到的只會是一個fakepath。有圖為證:
原理
FileReader就是html5為我們提供的讀取文件的api。它的作用就是把文本流按指定格式讀取到緩存,以供js調用。
FileReader有四種讀取文件的方式:
1.readAsBinaryString讀取為二進制碼
2.readAsDataURL讀取為 DataURL
3.readAsText讀取為文本
4.readAsArrayBuffer
根據本次實現的目標,使用第二種方式即可。img標簽的src就是這個圖片的編碼后的DataURL。如圖所示:
DataURL淺析
DataURL 說來可是有很多內容要研究的,但是這次用的比較淺顯,就把基礎的了解下就行了。
格式
DataURL有其固定的格式,如下:
data:[文件格式];base64,[文本流base64編碼]。
舉個例子:
•jpg格式: data:image/jpeg;base64,/9j/4...
•png格式: data:image/png;base64,iVBORw...
•gif格式: data:image/gif;base64,R0lGOD...
•png格式的圖片編碼信息
預覽實現
好了,弄明白了這些原理性的東西,就可以著手進行實現了。
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的一些新特性。用來過濾待上傳的圖片格式。
JavaScript控制
接下來就是預覽功能的實現了。目標就是將圖片轉換成DataURL,然后對預覽區(qū)進行子元素的添加操作。
<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>
預覽效果
總的來說代碼就算是完成了,接下來看下實現的效果。由于沒有設置樣式,所以看起來很簡陋,有興趣的自己用樣式控制一下即可。
打包封裝
簡易封裝
為了方便實用,特使用原生JavaScript封裝了一個這樣的組件。詳細代碼如下:
/** * 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; /** * 過濾圖片格式,可進行相對應的刪減操作。 * @type {{jpeg: string, gif: string, png: string}} */ this.filter = { /** * jpg或者jpeg格式的圖片。 */ "jpeg": "/9j/4", /** * gif格式的圖片。 */ "gif": "R0lGOD", /** * PNG格式的圖片。 */ "png": "iVBORw" }; /** * 開始預覽。自動調用原生JavaScript實現相關元素的定位以及渲染。 */ this.preview = function () { var file = document.getElementById(this.fileId); var container = document.getElementById(this.containerId); container.innerHTML = ""; /** * 防止內部作用域覆蓋問題。 * @type {ImgPrevirewer} */ var that = this; // HTML5 需要使用FileReader的相關API來讀取本地數據。 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 (!that.validateImg(srcpath)) { console.log(this.tip); } else { that.showPreviewImg(srcpath); } }; filereader.readAsDataURL(f); } } else { // 低版本降級處理。 if (!/\.jpg$|\.png$|\.gif$/i.test(file.value)) { console.log(this.tip); } else { that.showPreviewImg(file.value); } } } /** * 根據圖片的base64編碼格式查看圖片是否符合要求。 * @param data 編碼后的圖片數據。 * @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; } /** * 開始實現對圖片的預覽,根據this.imgStyle進行相關渲染操作。 * @param src */ this.showPreviewImg = function (src) { var img = document.createElement('img'); img.src = src; img.style = this.imgStyle; container.appendChild(img); } }
使用方式
下面來一個簡單的“模板式”使用技巧。
<!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: "請上傳格式為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>
測試
為了保證這個組件的穩(wěn)定性,接下來來個簡單的測試。
首先是在Chrome瀏覽器上,發(fā)現可以正常工作。
接下來是在Edge瀏覽器上的測試。(發(fā)現樣式不兼容)
不出所料,IE系的瀏覽器樣式都沒能兼容。
最終發(fā)現,Chrome等WebKit內核的瀏覽器可以完美支持,對于微軟系瀏覽器而言,功能可以滿足,但是樣式上不兼容,這點可以通過特定的瀏覽器頭來實現,不再過多敘述。
總結
總的來說,關于圖片上傳時的預覽功能,實用性還是很強的。對于一個網站可以算是一個加分項。當然了,該網站有一個設計感不錯的美工或者前端,不像我做出的頁面好難看(⊙﹏⊙)b。
大概就是這樣咯,有需要的盡管拿去使用。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Javascript技術難點之apply,call與this之間的銜接
這篇文章主要介紹了Javascript技術難點之apply,call與this之間的銜接的相關資料,需要的朋友可以參考下2015-12-12JavaScript鼠標事件監(jiān)聽、觸發(fā)時機和觸發(fā)順序實例講解
事件監(jiān)聽是Web開發(fā)中非常重要的一個概念,掌握了它的用法,可以讓我們實現更加豐富和動態(tài)的交互效果,這篇文章主要給大家介紹了關于JavaScript鼠標事件監(jiān)聽、觸發(fā)時機和觸發(fā)順序的相關資料,需要的朋友可以參考下2024-01-01使用JSX 建立組件 Parser(解析器)開發(fā)的示例
這篇文章主要介紹了使用JSX 建立組件 Parser(解析器)開發(fā)的示例(前端組件化)本文重點講述我們如何從0開始搭建一個組件系統(tǒng),基于標記語言的Parser的一種風格2021-04-04