iT邦幫忙

1

[筆記] Base64 & Web圖片上傳即時預覽

I. Base64

1. Intro.

  • 可用文字形式傳輸或儲存二進位資料(binary data),也包括MIME及XML
  • 每6個位元(bit)為一組Base64 unit,每個unit可對應 2^6=64 個可列印字元:A-Z, a-z, 0-9 & 2個視情況而定的符號
  • 一個binary data中,1字元 = 1位元組 (1byte) = 8bit,所以3字元 = 3byte = 24bit = 4個Base64 unit。因此編碼後字串長度會變成原本的 3/4 倍 (約略)
  • 若不是3的倍數則補 =:餘1補 ==、餘2補 =
  • 將3個二進位byte轉為4個Base64 units (from wiki)
    將3個二進位byte轉為4個Base64 units (from wiki)

2. In JavaScript

  • String encoded to base64 string: btoa()
    let binaryString1 = "Hello JS world!" // length = 15
    // btoa() 編碼為Base64
    btoa(binaryString1)
    // 輸出: "SGVsbG8gSlMgd29ybGQh", length = 20
    
    let binaryString2 = "Hello JS world" // length = 14 ... 餘2
    btoa(binaryString2)
    // 輸出: "SGVsbG8gSlMgd29ybGQ=", length = 20, 末端補 "="
  • Base64 decoded to string: atob()
    let base64String = "WW91IGNhdGNoZWQgbWUh"
    // atob() 解碼
    atob(base64String)
    // 輸出: "You catched me!"

II. Web API

1. FileReader物件

  • 非同步讀取client side檔案內容
  • 讀取結果會在FileReader.result物件中,形式則是看用哪個method讀取。例如:用readAsDataURL()則回傳Base64編碼字串;用readAsArrayBuffer()則回傳ArrayBuffer物件
  • 檔案來源: File object
    1. <input type="file"> 中取得的File
    2. drag/drop事件中的 event.originalEvent.dataTransfer.files

2. HTML部分:隨意建立一下

<style>
    // 給拖曳區畫個框線
    #dragArea {
        width:200px;
        border-style:dashed;
        border-width:0.2em;
        height:200px;
    }
    h4 {
        margin:20px auto 10px;
    }
</style>

// input瀏覽檔案
<h4>Upload image:</h4>
<form enctype="multipart/form-data">
    <input id="uploadImage" type="file" accept="image/*">
</form>

// 拖曳放置檔案
<h4>Or drop image here:</h4>
<div id="dragArea"></div>

// 預覽檔案
<h4>Preview:</h4>
<div id="previewDiv"></div>

3. JavaScript部分:

// 預覽圖片,將取得的files一個個取出丟到convertFile()
function previewFiles(files) {
    if (files && files.length >= 1) {
        $.map(files, file => {
            convertFile(file)
                .then(data => {
                  console.log(data) // 把編碼後的字串輸出到console
                  showPreviewImage(data, file.name)
                })
                .catch(err => console.log(err))
        })
    }
}

// 使用FileReader讀取檔案,並且回傳Base64編碼後的source
function convertFile(file) {
    return new Promise((resolve,reject)=>{
        // 建立FileReader物件
        let reader = new FileReader()
        // 註冊onload事件,取得result則resolve (會是一個Base64字串)
        reader.onload = () => { resolve(reader.result) }
        // 註冊onerror事件,若發生error則reject
        reader.onerror = () => { reject(reader.error) }
        // 讀取檔案
        reader.readAsDataURL(file)
    })
}

// 在頁面上新增<img>
function showPreviewImage(src, fileName) {
    let image = new Image(250) // 設定寬250px
    image.name = fileName
    image.src = src // <img>中src屬性除了接url外也可以直接接Base64字串
    $("#previewDiv").append(image).append(`<p>File: ${image.name}`)
}

// 當上傳檔案改變時清除目前預覽圖,並且呼叫previewFiles()
$("#upimg").change(function(){
    $("#previewDiv").empty() // 清空當下預覽
    previewFiles(this.files) // this即為<input>元素
})

// dragover事件:拖曳(滑鼠還沒鬆開)至放置區時,就要先防止瀏覽器進行開圖
$("#dragArea").on("dragover", function(e) {
    e.preventDefault()
})

// 當拖曳區發生drop事件時,接受格式為image的資料
$("#dragArea").on("drop", function(e){
    // 防止事件傳遞及預設事件發生
    e.stopPropagation()
    e.preventDefault()
    // 取得data type為image/*的資料
    e.originalEvent.dataTransfer.getData("image/*")
    let files = e.originalEvent.dataTransfer.files
    console.dir(files) // 看看File長什麼樣子
    $("#previewDiv").empty() // 清空當下預覽
    previewFiles(files)
})

4. 結果 & 備註

  1. 嘗試瀏覽圖片或是拖曳圖片後,可以在chrome console上看到FileReader讀出的Base64字串
  2. 不管使用哪個方法,所建立的file物件,都有基本properties可以查找相對應資料

III. 參考

Base64: https://zh.wikipedia.org/wiki/Base64
Web API/FileReader: https://developer.mozilla.org/zh-TW/docs/Web/API/FileReader
Web API/Image: https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image
Event/drop: https://developer.mozilla.org/en-US/docs/Web/Events/drop


尚未有邦友留言

立即登入留言