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>