2020-03-24

使用 JavaScript 互動載入檔案

HTML 的 input file 可以讓使用者簡單地載入本機電腦的檔案到 HTML 的表單資料
過去可很簡單地使用 JavaScript 將 input file 的檔案路徑,更新 DOM ,例如載入不同圖片變更顯示內容
但在下最近打算編寫一個類似的程式,發現不出錯誤……


基本 Input File


<input type="file"/>
見下文
原來基於安全理由,現今的網頁瀏覽器都不會在 input file 顯示完整檔案路徑
即使使用 JavaScript 讀取 input file 的 value 都只會顯示 檔案名C:\fakepath\檔案名
來保障使用者的目錄結構

<input type="file" onchange="window.alert(this.value);"/>

createObjectURL

如果需要達到互動載入檔案的效果,需要借用 createObjectURLWeb API
createObejctURL 是一種 HTML5 的 Web API 技術,能夠將資源轉換成 HTML 的 二進位大型物件 (Binary Large Object (blob)) 內容
createObjectURL 會傳回一個類似 base64 的字串,例如 blob:<domain>/6bda40ca-97e4-4f99-aef2-0d119bcbe3fe
實際這是一組 全域唯一識別碼 (Globally Unique Identifier (GUID))
這組字串只是一個二進制資料的 唯一ID ,指向保存在網頁瀏覽器記憶體中的資料
GUID 指向的資料 會在關閉網頁瀏覽器後 或執行 revokeObjectURL 在不關閉網頁瀏覽器的情況下釋放記憶體
而且由於資料轉換成 HTML 的 blob 並不會顯示使用者的系統結構,相對比較安全
但 createObjectURL 暫時未有統一實作的設計,因此會使用
function createObjectGUID(object) {
    if (window.createObjcectURL != undefined) {
        object = window.createOjcectURL(object);
    } else if (window.URL != undefined) {
        object = window.URL.createObjectURL(object);
    } else if (window.webkitURL != undefined) {
        object = window.webkitURL.createObjectURL(object);
    } else {
        object = null;
    }
    return object;
}

function deleteObjectGUID(guid) {
    if (window.revokeObjectURL != undefined) {
        window.revokeObjectURL(guid);
    } else if (window.URL != undefined) {
        window.URL.revokeObjectURL(guid);
    } else if (window.webkitURL != undefined) {
        window.webkitURL.revokeObjectURL(guid);
    }
}

function importImageFile(image, input) {
    var guid = createObjectGUID(input.files[0]);
    if (guid != null) {
        image.src = guid;
        if (image.parentNode instanceof HTMLAnchorElement) {
            image.parentNode.href = guid;
        }
    }
}
建立 或 刪除 GUID 及 被指派的 Object 資料

測試效果
Test Image
<a target="_blank"><img id="image-container" src="" width="600" height="400" alt="Test Image" title="Test Image"/></a>

<input type="file" onchange="importImageFile(document.getElementById('image-container'), this);"/>
input file 測試載入 HTML blob 到 HTML 圖像的效果

FileReader

FileReader 同樣是 HTML5 Web API 的技術之一,可以安全地透過 JavaScript 將檔案的內容在頁面上顯示
function importTextFile(text, input) {
    var fileReader = new FileReader();
    fileReader.readAsText(input.files[0]);
    fileReader.onload = function(event) {
        text.value = fileReader.result;
    };
}
讀取文件內容

測試效果

<textarea id="text-container" style="font-family: 'Courier New';" rows="10" cols="30" readonly="readonly"></textarea>

<input type="file" onchange="importImageFile(document.getElementById('text-container'), this);"/>
input file 測試載入 檔案內容 到 HTML textarea 的效果

檔案過濾

HTML5 新增 accept 屬性可以方便過濾檔案
accept 支援
  • MIME類型
    • 例如 image/pngvideo/mp4text/*
  • 副案名 (不需要使用 * (星號))
    • 例如 .png.mp4
設定多於一個過濾檔案類型,以 , (逗號) 分隔,例如 image/jpeg, .png

見下文
副案名就很簡單,但 MIME類型 每個系統支援都不相同
以 Firefox 為例,可以到 about:config 了解系統能夠支援的 MIME類型

見下文
了解系統支援的 MIME類型 ,按需要增加過濾內容

見下文
增加過濾內容後,選擇檔案時可以過濾檔案類型



<input type="file" onclick="this.accept = document.getElementById('file-accept').value;"/>
<label>過濾內容:<input id="file-accept" style="font-family: 'Courier New'; width: 600px;" type="text" value="audio/mpeg, audio/ogg, audio/x-wav, image/gif, image/jpeg, image/png, image/x-ms-bmp, video/mp4, video/ogg, video/webm, .aac"/></label>
借用 input text 來改變 input file 的 accept 內容,方便測試

總結

在下以前都曾經製作過類似的網頁程式,但當時沒有思考安全問題
直到今天再寫類似的程式時,發現不能正常運作,翻查資料才發現這個安全設計早在 2009年 已經修改,因此尋找解決方法
但有不少解決方法都要建議降低安全性以配合此操作
在下並不建議降低安全性來遷就這個程式的操作需要,而且使用者未必懂得修改安全性設定,因此還是要不斷尋找資料
而且還可以不需要借助 第三方工具起動網頁伺服器 便直接使用 HTML5 的 Web API 安全地存取檔案內容
設計互動網頁便更加簡單及方便

參考資料

沒有留言 :

張貼留言