Maklumat

Khusus untuk konten-konten sastra seperti puisi, cerpen dan esai silahkan kunjungi http://blog.edelweis-art.com. Terima kasih (Penulis)

Selasa, September 11, 2018

Mengunggah Foto dari Ponsel dengan HTML5, jQuery dan PHP

Di jaman mobile sekarang ini, pengembang aplikasi berbasis web pun dituntut harus bisa menyesuaikan diri. Di mana tampilan dan fitur yang dibuat, harus mobile friendly juga. Salah satu fitur yang biasa digunakan adalah mengunggah foto. Dan untuk kasus mobile device, kamera menjadi hal utama yang harus dipertimbangkan.

Beruntung, jika kita mau rajin mencari dan membaca, sebenarnya di internet telah tersedia banyak artikel, contoh dan panduan untuk pengembangan aplikasi yang kita buat. Tinggal kita sesuaikan saja dengan kebutuhan dan kemampuan nalar kita. Karena, tidak selalu yang kita dapatkan cocok dan bisa kita terapkan untuk aplikasi kita. Seperti halnya tentang pengunggahan foto dari ponsel baik dari data yang sudah ada maupun mengambil langsung menggunakan kamera, dari sekian yang saya baca, saya menemukan satu artikel yang saya anggap cocok. Dan melalui tulisan ini, saya ingin berbagi dengan Anda yang sedang memerlukannya.

Secara umum, proses pengunggahan foto (baik dari perangkat selular maupun komputer biasa), dapat dibagi menjadi 3 bagian, yaitu:

  1. Memilih atau mengambil foto
  2. Memfilter dan mempreview foto
  3. Mengunggah foto

Untuk memilih atau mengambil foto, kita bisa menggunakan elemen input pada HTML5. Kenapa mesti HTML5? Ya, karena pada HTML5 telah dimungkinkan untuk menginput foto atau file lebih dari satu sekaligus. Sementara untuk memfilter foto (biasanya mengatur ulang ukuran atau dimensi foto yang terasa cukup besar) serta mempreviewnya, bisa dilakukan di sisi klien maupun server. Namun, saya lebih suka melakukannya di sisi klien. Toh, jika kemudian pengguna bermaksud membatalkan unggahannya, tidak perlu sampai merecoki server. Untuk memfilter dan mempreview foto di sisi klien, kita bisa menggunakan jQuery. Dan untuk hal terakhir, yaitu proses pengunggahan--dan penyimpanan--foto ke server, kita pun bisa menggunakan jQuery yang kemudian akan diterima dan diteruskan prosesnya menggunakan PHP.

Berikut impelementasinya:

file index.html
<div>
  <input id="upload-image" type="hidden" /><input accept="image/*" id="image-file" multiple="" type="file" /> <button id="upload-file">Upload</button>
</div>
<div id="preview-image"></div>

file upload.js
$(document).ready(function(){
 canvasLastID = 0;

 $(document).on("change", '#image-file', function(e) {
  var files = e.target.files;

  $.each(files, function(index,value){
   if(value) {
    if(/^image\//i.test(value.type)) {
     readFile(value,canvasLastID);
     canvasLastID++;
    } else {
     alert("Oops! Invalid file type");
    }
   }
  });

  $(this).val('');
 });

 $(document).on("click",".delete-canvas",function(){
  var index = $(this).attr('data-id');
  var dataURL = $(this).attr('data-content');

  var attachments = $("#upload-image").val();
  attachments = JSON.parse(attachments);

  var iO = attachments.indexOf(dataURL);
  attachments.splice(iO,1);

  var n = attachments.length;

  if(n == 0) {
   $("#upload-image").val('');
   $("#preview-image").empty();
   canvasLastID = 0;
  } else {
   attachments = JSON.stringify(attachments);
   $("#upload-image").val(attachments);
   $('.canvas-box[data-id='+index+']').remove();
  }
 });

 $(document).on("click","#upload-file",function(e){
  e.preventDefault();

  var data = {};
  data.imgs = $("#upload-image").val();

  if(data.imgs=="") {
   alert("Please select atleast one photo");
  } else {
   $.post("upload.php", data, function(data) {
    var data = JSON.parse(data);

    if(data.status==1) {
     $("#upload-image").val('');
     $("#preview-image").empty();
     canvasLastID = 0;
    }

    alert(data.message);
   });
  }

  return false;
 });
});

function readFile(file,index) {
 var reader = new FileReader();

 reader.onloadend = function () {
  processFile(reader.result, file.type, index);
 }

 reader.onerror = function () {
  alert("Oops! An error has occurred");
 }

 reader.readAsDataURL(file);
}

function processFile(dataURL, fileType, index) {
 var maxWidth = 800;
 var maxHeight = 800;

 var maxIconWidth = 150;
 var maxIconHeight = 150;

 var image = new Image();
 image.src = dataURL;

 image.onload = function () {
  var width = image.width;
  var height = image.height;

  if ((width > maxWidth) || (height > maxHeight)) {
   var newWidth;
   var newHeight;

   if (width > height) {
    newHeight = height * (maxWidth / width);
    newWidth = maxWidth;
   } else {
    newWidth = width * (maxHeight / height);
    newHeight = maxHeight;
   }

   var canvas = document.createElement('canvas');

   canvas.width = newWidth;
   canvas.height = newHeight;

   var context = canvas.getContext('2d');

   context.drawImage(this, 0, 0, newWidth, newHeight);

   dataURL = canvas.toDataURL(fileType);
  }

  // create icon images
  var newIconWidth;
  var newIconHeight;

  if ((width > maxIconWidth) || (height > maxIconHeight)) {
   if (width > height) {
    newIconHeight = height * (maxIconWidth / width);
    newIconWidth = maxIconWidth;
   } else {
    newIconWidth = width * (maxIconHeight / height);
    newIconHeight = maxIconHeight;
   }
  } else {
   newIconHeight = height;
   newIconWidth = width;
  }

  var canvas2 = document.createElement('canvas');
  canvas2.id = 'ImgIcon'+index;

  canvas2.width = newIconWidth;
  canvas2.height = newIconHeight;

  var context2 = canvas2.getContext('2d');

  context2.drawImage(this, 0, 0, newIconWidth, newIconHeight);

  // push dataURL to attachments
  var attachments = $("#upload-image").val();

  if(attachments=="") {
   attachments = [dataURL];
  } else {
   attachments = JSON.parse(attachments);
   attachments.push(dataURL);
  }

  attachments = JSON.stringify(attachments);

  $("#upload-image").val(attachments);

  // add icon image to preview image box
  $("#preview-image").append('<div class="canvas-box" data-id="'+index+'" style="width:'+newIconWidth+'px;height:'+newIconHeight+'px"></div>');
  $('.canvas-box[data-id='+index+']').append(canvas2);
  $('.canvas-box[data-id='+index+']').append('<span class="text-red delete-canvas" data-id="'+index+'" data-content="'+dataURL+'" style="position:absolute;top:4px;right:6px;z-index:99"><img src="icons/close.png" /></span>');
 };

 image.onerror = function () {
  alert("Oops! An error has occurred");
 };
}

file upload.php
$time = date('YmdHis');
$return = array();

function saveFromBase64($imageData,$newImage) {
 $imageData = explode('base64,', $imageData);
 $imageBase64 = $imageData[1];
 $imageString = base64_decode($imageBase64);
 $img = imagecreatefromstring($imageString);

 if($img !== false) {
  $path_parts = pathinfo($newImage);
  $extension = strtolower($path_parts['extension']);

  if($extension=='jpg' || $extension=='jpeg') {
   $save = imagejpeg($img, $newImage);
  } elseif($extension=='png') {
   $save = imagepng($img, $newImage);
  } else {
   $save = false;
  }

  imagedestroy($img);

  if($save) {
   return true;
  } else {
   return false;
  }
 } else {
  return false;
 }
}

if(isset($_POST['imgs']) && $_POST['imgs']!="") {
 $imgs = json_decode($_POST['imgs']);

 $count = 0;
 $success = 0;
 $failed = 0;

 $n = 0;

 foreach ($imgs as $img) {
  $n++;

  $filename = "images/img-$time-$n.png";
  $saved = saveFromBase64($img,$filename);

  if($saved) {
   $success++;
  } else {
   $failed++;
  }
 }

 $count = $n;

 if($count==$failed) {
  $return['status'] = 0;
  $return['message'] = "Oops! An error has occurred. No photos uploaded";
 } elseif($count==$success) {
  $return['status'] = 1;
  $return['message'] = "Yes! All photos uploaded";
 } else {
  $return['status'] = 2;
  $return['message'] = "$success of $count photos uploaded";
 }
} else {
 $return['status'] = 0;
 $return['message'] = "Invalid request";
}

echo json_encode($return);

Mula-mula, foto-foto yang akan diunggah, dicek dulu dimensinya. Dalam hal ini kita buat dimensi maksimal foto yang akan diunggah misalnya adalah width=800px dan height=800px. Artinya, jika dimensi foto melebihi ukuran tersebut, maka foto akan dikecilkan terlebih dahulu menggunakan canvas HTML5. Foto-foto tersebut lalu dikonversi menjadi data url yang akan dikirimkan ke server. Dan untuk memudahkan pengguna, sebelum foto-foto tersebut dikirim, kita buatkan preview-nya dalam ukuran-ukuran yang lebih kecil, misal width & height 150px menggunakan canvas HTML5. Di sini pengguna dapat menghapus juga foto-foto yang keliru dipilih. Setelah diterima di server, data url foto kemudian dikembalikan menjadi file image untuk disimpan.

Demikian, mudah-mudahan bermanfaat. Jika Anda membutuhkan source code-nya, silahkan klik di sini untuk mengunduhnya.





Kamis, Juli 26, 2018

Cara Mencetak Data Secara Langsung dari Web Browser Android

Salah satu kendala yang kita hadapi sebagai developer aplikasi web adalah mencetak langsung dari browser pada perangkat Android (misalnya untuk mencetak faktur penjualan, bukan mencetak halaman web) di mana jika kita menggunakan browser pada desktop sangat dimungkinkan dengan menggunakan javascript namun untuk web browser pada Android hal ini tidak berlaku (You cannot use JavaScript in a HTML document to trigger printing).

Bagi yang menguasai juga pembuatan aplikasi Android, mungkin hal di atas tidak menjadi kendala yang cukup berarti. Namun bagi yang tidak menguasai, kendala di atas bisa menjadi mimpi buruk yang mengancam, karena di jaman sekarang, tentu banyak klien yang menginginkannya. Hal yang bisa dilakukan pada akhirnya adalah meng-hire developer android untuk membuatkan aplikasi yang bisa membantu atau mencari-cari aplikasi di Play Store yang bisa digunakan. Untuk hal yang ke dua, tentu membutuhkan proses trial and error dan keberuntungan tentu saja. Ya, sebab jika tidak beruntung kita akan cukup lama menemukan aplikasi yang cocok. Karena dari pengalaman saya pribadi, aplikasi-aplikasi untuk mencetak (yang biasanya melalui bluetooth dan disambungkan dengan printer thermal yang biasanya juga disertakan saat kita membeli printernya) tidak banyak yang membantu. Rata-rata bisa digunakan hanya melalui aplikasi itu sendiri. Dan beruntung, saya menemukan sebuah aplikasi di Play Store, Bluetooth Print, yang dikembangkan oleh Mate Technologies. Aplikasi ini menyediakan fungsi yang bisa kita gunakan mencetak langsung dari halaman web pada perangkat Android kita.



Untuk mencetak langsung dari browser menggunakan aplikasi Bluetooth Print tersebut, kita bisa menggunakan tag hyperlink atau fungsi window.open() pada halaman web.





Secara detail, prosesnya bisa digambarkan sebagai berikut:

Saat kita klik link atau tombol cetak pada halaman web, maka akan membuka aplikasi Bluetooth Print. Selanjutnya aplikasi akan mengirimkan request ke url yang telah kita tentukan dan simpan sebelumnya, yang akan memberikan response berupa data yang akan dicetak dalam bentuk json. Jika url dan data response valid, maka aplikasi akan meneruskan datanya ke printer untuk dicetak.

Adapun link address untuk membuka aplikasi Bluetooth Print adalah my.bluetoothprint.scheme://<url>?<parameter>. <url> lazimnya diisi dengan url halaman web kita namun dapat diisi apa saja sementara <parameter> adalah parameter yang akan dikirim ke url response yang kita tentukan. Misalnya link address-nya adalah my.bluetoothprint.scheme://http://ekasir.namatokokita.com/form-penjualan.php?nota=0001. Sementara url response-nya misalnya http://ekasir.namatokokita.com/data.php. Maka saat kita klik link address tersebut, aplikasi akan mengirimkan GET request nota=0001 yang akan kita tangkap di http://ekasir.namatokokita.com/data.php dan kita gunakan sebagai parameter untuk mengirimkan  data yang diperlukan. Bagaimana bentuk data yang harus dikirimkan, pada pengaturan aplikasi telah disediakan contohnya.

Demikian, mudah-mudahan bermanfaat.