Last updated: 2023-09-23 
Logo hb

Introduction

The File-Uploader 'Plupload' (https://www.plupload.com/) allows you to conveniently select files from a browser window, which are then uploaded to a cloud or your own web space. A progress bar can be displayed for each individual file.

In the common examples, after selecting the files to upload, the file names of all selected files are displayed in a list. Often it involves uploading images and there is a desire to display a preview image for each selected image file in addition to the file names. This would give you another good visual check to see whether you have selected the right images before you start the upload.

Here we will present a simple solution on how to display a preview image in addition to each file name in the selection list by making a small change to the Javascript code.

Download the sample files

The complete and fully functional example can be downloaded here:
Download 'uploader.zip'    (Last updated: 2023-09-23)

Instructions:

  1. Unpack ZIP file
  2. Upload everything to your own web space,
    for example to a folder called 'Uploader'
Complete.

You can now access the website “https://MyDomain.com/Uploader” in your Internet browser and upload files.

When using the Internet, additional security mechanisms must definitely be integrated, e. g. a login system.

1.  The file 'index.html'

After calling "https://MyDomain.com/Uploader" the website initially looks like this:

Appearance of the uploader after opening the HTML file

After clicking on "BROWSE..." and selecting images you will see the list with file names and preview images:

Appearance of the uploader after selecting images

After clicking on the “START” button, the images will be uploaded with a progress display:

Appearance of the uploader after starting the upload

For files that are not images, icons are displayed:

Icons for files that are not images

The file 'index.html'
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex" />
<title>Uploader</title>
<link rel="stylesheet" type="text/css" href="css/index.css">
<link rel="stylesheet" type="text/css" href="css/mobile.css">
<script type="text/javascript" src="js/plupload.full.min-2.3.9.js"></script>
<script type="text/javascript" src="js/jquery-1.11.1.js" charset="UTF-8"></script>
</head>

<body>
<header>
  <div id="title">
    <h1>Uploader</h1>
  </div>
</header>

<div id="container">
  <div id="hint">
    <div id="hinttext">
      <h3>Choose files</h3>
      <h4>Choose files and click START</h4>
    </div>
  </div>

  <div id="filelist">
    <table></table>
  </div>

  <div id="buttons">
    <button id="btnBrowse">BROWSE...</button>
    <button id="btnUpload" disabled="disabled">START</button>
    <button id="btnClear" disabled="disabled">CLEAR LIST</button>
  </div>
 
  <div id="console"></div>
</div>  <!-- "container" -->
<script type="text/javascript" src="js/uploader.js"></script>
</body>
</html>

Calling up this HTML file would still have to be protected by a login system when used on the Internet.

2.  The file 'uploader.js'

var receiver = 'receiver.php';
var maxNumFiles = 100;
var maxFileSize = '30 MB';
var console = $('#console');
var tableHeader = '<tr><th>File</th><th>Size</th><th>Del.</th><th>Upload</th></tr>';
var fileCountOld = 0;
var fileIndex = 0;
var maxLength;
var innWidth;

var filter = 
    {
      max_file_size : maxFileSize,
      mime_types: [
        {title : "Image files", extensions : "jpg,jpeg,gif,png,bmp,tif,tiff,svg"},
        {title : "Pdf files", extensions : "pdf"},
        {title : "Office files", extensions : "doc,docx,xlsx,pptx,pub,odt"},
        {title : "Text files", extensions : "txt"},
        {title : "Media files", extensions : "wav,mp3,mp4,ogg"},
        {title : "Zip files", extensions : "zip,rar"}
      ]
    };

var uploader = new plupload.Uploader({
  browse_button: 'btnBrowse',
  url: receiver,
  max_files: maxNumFiles,
  filters : filter
});

uploader.init();

uploader.bind('FilesAdded', function(up, files) {
  var row;
  var re = /(?:\.([^.]+))?$/;
  if (up.files.length <= up.settings.max_files) {
    fileIndex = 0;
    plupload.each(files, function(file) {
      var ext = re.exec(file.name)[1];
      row = '<tr id="' + file.id + 
            '"><td class="first_col"><img id="im' + file.id + '" src="" width="auto" height="60" /><br />' + file.name.substr(0, maxLength) + 
            '</td><td>' + plupload.formatSize(file.size) + 
            '</td><td><a href="javascript:;" id="a' + file.id + '" class="delete"><img src="pics/Delete.png" class="deletePic" /></a>'+
            '</td><td><div class="progressBorder"><div class="progress"></div></div><span class="percent"></span>'+
            '</td></tr>';
      $('#filelist table').append(row);

      if (ext.toLowerCase() == "jpg" 
        || ext.toLowerCase() == "jpeg"
        || ext.toLowerCase() == "png" 
        || ext.toLowerCase() == "gif" 
        || ext.toLowerCase() == "bmp") {
        const reader = new FileReader();
        reader.addEventListener('load', () => {
            document.getElementById('im' + file.id).src = reader.result;
        });
        reader.readAsDataURL(files[fileIndex].getSource().getSource());
      } else if (ext.toLowerCase() == "mp4") {
        document.getElementById('im' + file.id).src = "pics/VideoSymbol.png";
      } else if (ext.toLowerCase() == "mp3" || ext.toLowerCase() == "wav") {
        document.getElementById('im' + file.id).src = "pics/MusicSymbol.png";
      } else if (ext.toLowerCase() == "zip") {
        document.getElementById('im' + file.id).src = "pics/ZIP-Symbol.png";
      } else if (ext.toLowerCase() == "pdf") {
        document.getElementById('im' + file.id).src = "pics/PDF-Symbol.png";
      } else if (ext.toLowerCase() == "xlsx" || ext.toLowerCase() == "xlsm") {
        document.getElementById('im' + file.id).src = "pics/Excel-Symbol.png";
      } else if (ext.toLowerCase() == "docx" || ext.toLowerCase() == "doc") {
        document.getElementById('im' + file.id).src = "pics/Word-Symbol.png";
      } else if (ext.toLowerCase() == "pub") {
        document.getElementById('im' + file.id).src = "pics/Publisher-Symbol.png";
      } else if (ext.toLowerCase() == "txt") {
        document.getElementById('im' + file.id).src = "pics/TXT-Symbol.png";
      } else {
        document.getElementById('im' + file.id).src = "pics/FileSymbol.png";
      }
      fileIndex = fileIndex + 1;

    });
    plupload.each(files, function(file) {
        $('#' + file.id + ' .progressBorder').hide();
        document.getElementById('a' + file.id).onclick = function() {
          if (up.state == plupload.STARTED && file.status == plupload.UPLOADING) {
            up.stop();
            up.removeFile(file);
            up.start();
          } else {
            up.removeFile(file);
          }
          $('#' + file.id).hide(500);
          if (up.files.length == 0) {
            $('#btnClear').prop('disabled', true);
          }
        };
    });
    fileCountOld = up.files.length;
  } else {
    var str;
    var anzahl = up.settings.max_files - fileCountOld;
    if (anzahl > 1) {
      str = "Es können höchstens noch " + anzahl + " Dateien hinzugefügt werden.<br />"
    } else 
    if (anzahl == 1) {
      str = "Es kann höchstens noch 1 Datei hinzugefügt werden.<br />"
    } else {
      str = "Es kann keine Datei mehr hinzugefügt werden.<br />"
    }
    console.append("Die Liste kann höchstens " + up.settings.max_files + " Dateien enthalten!<br />" + str);
    up.splice(fileCountOld);
  }
  $('#btnClear').prop('disabled', false);
});

uploader.bind('QueueChanged', function (up) {
  if (uploader.files.length == 0) {
    $('#btnUpload').prop('disabled', true);
  } else {
    $('#btnUpload').prop('disabled', false);
    $('#btnUpload').focus();
  }
  if (up.files.length < fileCountOld) {
    fileCountOld = up.files.length;
  }
}); 

uploader.bind('BeforeUpload', function(up, file) {
  $('#btnUpload').prop('disabled', true);
  $('#' + file.id + ' .progressBorder').show();
});

uploader.bind('UploadProgress', function(up, file) {
  $('#' + file.id + ' .percent').html(file.percent + '%');
  $('#' + file.id + ' .progress').css('width', file.percent + '%');
});

uploader.bind('FileUploaded', function(up, file, res) {
  $('#' + file.id + ' .progressBorder').hide(300);
  setTimeout(setOkHook, 400, file.id);
});

function setOkHook(id) {
  $('#' + id + ' .percent').append(" <img src='pics/Ok.png' class='picOk' />");
  $('#a' + id).hide(100);
}
 
uploader.bind('UploadComplete', function (up, file) {
  $('#btnUpload').prop('disabled', true);
  $('#kommentarinput').val("");
  $('#kommentaranzeige').css('display', 'none');
});

uploader.bind('Error', function(up, err) {
  var filesize = strFileSize(err.file.size);
  var meldung = "<span class='filename'>" + err.file.name + "</span><br />";
  switch (err.code) {
    case -600:
      if (err.file.size > 0) {
        meldung += "Datei zu groß: " + filesize + " (max. " + filter.max_file_size + ")<br />";
      } else {
        meldung += "Dateigröße ist 0 Bytes!<br />";
      }
      break;
    case -601:
      meldung += "Unerlaubte Dateiendung!<br />";
      break;
    default:
      meldung += "Error" + err.code + ": " + err.message + " (Filesize: " + filesize + ")<br />";
  }
  meldung += "<br />";
  $('#console').append(meldung);
});
 
$('#btnBrowse').click(() => {
  console.html("");
});

$('#btnUpload').click(() => {
  uploader.start();
  console.html("");
});

$('#btnClear').click(() => {
  $('#filelist table').html(tableHeader);
  uploader.splice(0, uploader.files.length);
  $('#btnClear').prop('disabled', true);
  console.html("");
  fileCountOld = 0;
  return false;
});

function strFileSize(bytes, decimalPlaces = 1, separator = ' ') {
    if (bytes) {
        const sizes = ['Bytes', 'kB', 'MB', 'GB', 'TB'];
        const i = Math.min(parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString(), 10), sizes.length - 1);
        return `${(bytes / (1024 ** i)).toFixed(i ? decimalPlaces : 0)}${separator}${sizes[i]}`;
    }
    return '0 Bytes';
}

$(window).resize(function() {
    setMaxLength();
});

function setMaxLength() {
    innWidth = $(window).width();
    if (innWidth <= 480) {
        maxLength = 32;
    } else {
        maxLength = 70;
    }
}

setMaxLength();
$('#btnUpload').prop('disabled', true);  // for page refresh (F5)
$('#btnClear').prop('disabled', true);   //      "
$('#btnBrowse').focus();

$('#filelist table').html(tableHeader);

To make the preview image visible, two places in the uploader.bind('FilesAdded', ...) method are crucial.

The line that begins with "row = ..." compiles the individual lines for displaying the upload list:

row = '<tr id="' + file.id + 
      '"><td class="first_col"><img id="im' + file.id + '" src="" width="auto" height="60" /><br />' + file.name.substr(0,
      . . .
      $('#filelist table').append(row);

An img tag is generated in each line, the ID of which consists of the letters "im" and the file ID generated internally by the Pluploader. This DOM element can then be addressed using this ID and the initially empty src attribute can be filled.

This happens within the immediately following IF query:

      if (ext.toLowerCase() == "jpg" 
        || ext.toLowerCase() == "jpeg"
        || ext.toLowerCase() == "png" 
        || ext.toLowerCase() == "gif" 
        || ext.toLowerCase() == "bmp") {
        const reader = new FileReader();
        reader.addEventListener('load', () => {
            document.getElementById('im' + file.id).src = reader.result;
        });
        reader.readAsDataURL(files[fileIndex].getSource().getSource());
      } else if (ext.toLowerCase() == "mp4") {

There it is checked whether the file is an image. If so, a new instance of the 'FileReader' class is created with the statement const reader = new FileReader();.

In the following line reader.addEventListener('load',...), this FileReader instance is assigned an event listener for the load event.

The reader.readAsDataURL(files[fileIndex].getSource().getSource()); instruction reads the contents of the image and stores it in base64 format in the variable reader.result. At the end of the reading process, the load event triggers a call to the load event listener, in which the src attribute is now not filled with an image address, but with the image data stored in base64 format. This has the effect of causing the image to be displayed by the browser.

3.  The file 'receiver.php'

The pluploader sends the selected files (or more precisely an HTML multipart request) to the server-side file 'receiver.php'.

<?php
$targetDir = 'uploads/' . date("Y-m-d_H.i");
if (!file_exists($targetDir)) {
	@mkdir($targetDir);
}

$fileName = $_FILES["file"]["name"];
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;

move_uploaded_file($_FILES["file"]["tmp_name"], $filePath);
?>

This PHP script creates a subfolder in the 'uploads' folder for each upload, the name of which is made up of the current date and time, for example '2023-09-22_15.33'. All uploaded files will then be saved in this subfolder with their original file name.

The login system also needs to be integrated into this PHP script if it is to be used on the Internet.

Ornament