2020年9月21日 星期一

即時預覽圖片、多檔上傳 (with no bootstrap, no JQuery)


程式碼

<!DOCTYPE html>

<html lang=zh-Hant>

<head>

<title>preview picture with no bootstrap no jQuery</title>

    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

</head>

<style>  

#OriginInput{

border:1px solid red;

height:30px;  

opacity:100; /* 必須為 0,覆蓋的「綠色圖片按鈕」才有作用 */

  width:90px;

        cursor:pointer;


}  

    #click_me{/*這是圖片,來當覆蓋按鈕*/

    margin-top:0px;

    margin-left:-60px;

        opacity:100; /* 0:不顯示 */

        /* cursor:pointer; 没作用,被 #OriginInput 強制取代 */

    }


</style>  

<body>

(1) 三種方式取得上傳圖片資料:二進位碼字串、UTF-8字串、Base64編碼字串。<br />

    (2) 上傳圖檔絕對路徑不正確,基本是瀏覽器之安全設定問題<br />

    (3) 綠色圖片游標變手型時,點按可上傳圖檔 但CSS要先設定 OriginInput.opacity = 0<br />

    (4) 預覽圖片可依需求動態調整大小<br />

    (5) 本程式已有多檔上傳功能,略改寫就能達到<br />

    <br />

    

    <div id="result" style="border:1px solid red;">測試結果區</div>

<form action="後台程式名.php" method="post" enctype="multipart/form-data">

<input id="PretendInput" type="text" readonly >

<input id="OriginInput" type="file"  multiple accept="image/gif, image/jpeg, image/png"  onchange="setFilePath()" >

<img id="click_me" src="https://via.placeholder.com/100x35/00FF00/" ><br />

</form>

</body>

<script>

function setFilePath(){


//--- 取得單一檔案路徑 + 檔名 - 第1種正確 => 

        var oOriginInput=document.getElementById("OriginInput");

        var oFiles=document.getElementById("OriginInput").files[0];

        if(oFiles && oFiles.name.trim().length > 4){

            let reader = new FileReader();


/*

            //-- 開始:用二進位字串顯示圖片資料 (讀出圖片16進位資料)

reader.readAsBinaryString(oFiles);

reader.onload = function(e) {

let vData = e.target.result;

                document.getElementById("result").innerHTML = null;

document.getElementById("result").innerHTML = vData;

};

            //-- 結束

*/

/*

            //-- 開始:用UTF-8字串顯示圖片資料 (讀出圖片16進位資料,呈現很多都無字型碼)

reader.readAsText(oFiles,'UTF-8');

reader.onload = function (e) {

let vData = e.target.result;

                document.getElementById("result").innerHTML = null;

document.getElementById("result").innerHTML = vData;

};

            //-- 結束

*/


            //-- 開始:解譯Base64編碼,直接預覽圖片 (讀出圖片16進位資料)

            reader.readAsDataURL(oFiles);

            reader.onload = function (e) {

                let vData = e.target.result;

                

                //let vData = document.getElementById("OriginInput").result; <= 錯誤語法

                //說明:(1) e.target == document.getElementById("OriginInput"), 但是 input

                //         没有 result 屬性。

                //     (2) 判斷 e.target 應該是最早取得結果值的物件,而 OriginInput 則是較後期才

                //         取得上傳的資料(或由e.target 把部份值塞給 OriginInput 的屬性變數)。

                

                //塞入完整「檔案路徑 + 檔名」

                document.getElementById("PretendInput").value=oOriginInput.value;

                            

                //預覽檔名

                document.getElementById("result").innerHTML =null;

                document.getElementById("result").innerHTML =oFiles.name + " <br /> ";

                

                //預覽圖片 (以下這段程式中的 vData 資料不是字串,是base64之圖片編碼)

                document.getElementById("result").innerHTML += "<img src='" + vData + "' style='width:120px;height:120px;'  alt='" + oFiles.name + "' />";

            };         

        //-- 結束


        }else{

        document.getElementById("PretendInput").value="";

        }



    

/*

        //--- 第2種 -- 取得單一「檔案路徑 + 檔名」之結果不正確 => C:\fakepath\sa.png

        var oFilePath=document.getElementById("OriginInput");       

        if(oFilePath.value.trim().length > 0){

document.getElementById("PretendInput").value=oFilePath.value;

alert("取得檔案路徑及檔名:" + oFilePath.value);

        }else{

        document.getElementById("PretendInput").value="";

        }

*/        


/*

        //--- 取得檔案名稱(適合多檔上傳架構)

        var oFiles=document.getElementById("OriginInput").files;

        var j=oFiles.length;

//console.log("共 " + j + " 個檔名");


        for(let i=0;i<j;i++){

if (oFiles[i]){

//alert("取得第 " + parseInt(i+1) + " 個檔名:" + oFiles[i].name.trim());

                

//以下三個有反應

console.log("取得第 " + parseInt(i+1) + " 個 name:" + oFiles[i].name.trim());

console.log("取得第 " + parseInt(i+1) + " 個 type:" + oFiles[i].type.trim());

console.log("取得第 " + parseInt(i+1) + " 個 size:" + oFiles[i].size);

                

//以下二個 undefined                

console.log("取得第 " + parseInt(i+1) + " 個 tmp_name:" + oFiles[i].tmp_name);

console.log("取得第 " + parseInt(i+1) + " 個 error:" + oFiles[i].error);

}

}

*/


/*

        //--- 取得檔案名稱(適合單檔上傳架構)

        var oFiles=document.getElementById("OriginInput").files;

        

//if(oFiles && oFiles.length>0 && sFileName.length > 0){

        if(oFiles){

        let sFileName=oFiles[0].name.trim();

        document.getElementById("PretendInput").value=sFileName;

        alert("取得檔案名稱:" + sFileName);

        }else{//清空檔名

        document.getElementById("PretendInput").value="";

        }

*/

}  

</script>

</html>

2020年9月20日 星期日

即時預覽圖片、單檔上傳 (with bootstrap & JQuery)








原始程式碼:

<!DOCTYPE html>

<html lang=zh-Hant>

<head>

<title>picture upload with bootstrap and jQuery</title>

    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet" type="text/css">

<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" ></script>

<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"></script>

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" ></script>

</head>

<style>

  .image-preview-wrapper{

    display: block;

    max-width: 310px;

    max-height: 310px;

    width: 100%;

    border: 2px solid #cccccc;

    margin: 0 auto;

    position: relative;

    cursor: pointer;

  }


  .spinner-wrapper {

    opacity: 0;

    margin: 0;

    padding: 0;

    left: 50%;

    top: 50%;

    transform: translate(-50%, -50%);

  }


  .opacity-1 {

    opacity: 1;

  }

  

  #idSubmit{

  cursor:pointer;

  }

  

  #file-uploader, #idFilePath{

  cursor:pointer;

  }


</style>


<body>

<br /><br />

<div class="row">

<div class="col-12 col-md-6 mx-auto">

  <a style="float:left" href="https://pjchender.blogspot.com/2019/01/js-javascript-input-file-upload-file.html">程式來源</a>

</div></div>            

<br /><br />

    

<label class="text-center mb-5 image-preview-wrapper" for="file-uploader">

<img src="https://via.placeholder.com/500x300?text=click to upload" alt="image-placehoder" class="img-thumbnail" data-target="image-preview">

    <!-- loading --->

<div class="spinner-wrapper position-absolute" data-target="spinner">

<div class="spinner-border text-secondary" role="status">

<span class="sr-only">Loading...</span>

</div>

</div>

</label>


  

<div class="row">

<div class="col-12 col-md-6 mx-auto">

<div class="custom-file">

<form action="後台程式名.php" method="post" enctype="multipart/form-data">

<input type="file" class="custom-file-input"  id="file-uploader" multiple accept="image/gif, image/jpeg, image/png" data-target="file-uploader" onchange="setFilePath()" >

<label class="custom-file-label"  id="idFilePath">點按上傳檔案</label>

                    <br /><br />

                    <input type="submit" id="idSubmit" value="Submit">

</form>

</div>

</div>

</div>


</body>


<script>

function setFilePath(){

    document.getElementById("idFilePath").innerText=document.getElementById("file-uploader").value;

    }

</script>


<script>

// STEP 1: select element and register change event

const imagePreview = document.querySelector('[data-target="image-preview"]');

const spinner = document.querySelector('[data-target="spinner"]');

const fileUploader = document.querySelector('[data-target="file-uploader"]');

fileUploader.addEventListener("change", handleFileUpload);


async function handleFileUpload(e) {

  try {

const file = e.target.files[0];

setUploading(true);

if (!file) return;


const beforeUploadCheck = await beforeUpload(file);

if (!beforeUploadCheck.isValid) throw beforeUploadCheck.errorMessages;


const arrayBuffer = await getArrayBuffer(file);

const response = await uploadFileAJAX(arrayBuffer);

alert("File Uploaded Success");

showPreviewImage(file);

  } catch (error) {

alert(error);

console.log("Catch Error: ", error);

  } finally {

e.target.value = '';  // reset input file

setUploading(false);

  }

}


// STEP 2: showPreviewImage with createObjectURL

// If you prefer Base64 image, use "FileReader.readAsDataURL"

function showPreviewImage(fileObj) {

  const image = URL.createObjectURL(fileObj);

  imagePreview.src = image;

}


// STEP 3: change file object into ArrayBuffer

function getArrayBuffer(fileObj) {

  return new Promise((resolve, reject) => {

const reader = new FileReader();

// Get ArrayBuffer when FileReader on load

reader.addEventListener("load", () => {

  resolve(reader.result);

});


// Get Error when FileReader on error

reader.addEventListener("error", () => {

  reject("error occurred in getArrayBuffer");

});


// read the blob object as ArrayBuffer

// if you nedd Base64, use reader.readAsDataURL

reader.readAsArrayBuffer(fileObj);

  });

}


// STEP 4: upload file throguth AJAX

// - use "new Uint8Array()"" to change ArrayBuffer into TypedArray

// - TypedArray is not a truely Array,

//   use "Array.from()" to change it into Array

function uploadFileAJAX(arrayBuffer) {

  // correct it to your own API endpoint

  return fetch("https://jsonplaceholder.typicode.com/posts/", {

headers: {

  version: 1,

  "content-type": "application/json"

},

method: "POST",

body: JSON.stringify({

  imageId: 1,

  icon: Array.from(new Uint8Array(arrayBuffer))

})

  })

.then(res => {

  if (!res.ok) {

throw res.statusText;

  }

  return res.json();

})

.then(data => data)

.catch(err => console.log("err", err));

}


// STEP 5: Create before upload checker if needed

function beforeUpload(fileObject) {

  return new Promise(resolve => {

const validFileTypes = ["image/jpeg", "image/png"];

const isValidFileType = validFileTypes.includes(fileObject.type);

let errorMessages = [];


if (!isValidFileType) {

  errorMessages.push("You can only upload JPG or PNG file!");

}


const isValidFileSize = fileObject.size / 1024 / 1024 < 2;

if (!isValidFileSize) {

  errorMessages.push("Image must smaller than 2MB!");

}


resolve({

  isValid: isValidFileType && isValidFileSize,

  errorMessages: errorMessages.join("\n")

});

  });

}


function setUploading(isUploading) {

  if (isUploading === true) {

spinner.classList.add("opacity-1");

  } else {

spinner.classList.remove("opacity-1");

  }

}

</script>

</html>