Come implementare un file uploader in maniera semplice e senza impazzire.

Ora indovino: se sei un web developer /designer, e ti spingi al di là di accettare passivamente i plugin e le altre risorse messe a disposizione da cms (WordPress, Joomla, Drupal, ecc. ), ti sarà capitato di fare la stessa espressione dell'immagine di questo articolo, quando hai avuto a che fare con i web form che richiedono il caricamento di immagini o file in generale. 

Perchè diciamocelo, il tag <input type="file" /> di per sè è davvero brutto. Non solo, ma anche difficile da personalizzare. In questo articolo vedremo come con jQuery possiamo realizzare una sovrastruttura (un wrapper) che non solo abbellisce un pò il tag in questione, ma permette anche di avere l'anteprima dell'immagine PRIMA che essa sia effettivamente caricata sul server. 

Per le parti lato server, in questo articolo sarà usato php, ma ovviamente il codice si può adattare con qualsiasi framework/cms voi vogliate.

Iniziamo.

per prima cosa, creiamo la seguente struttura:

Listato 1.1
<div style="width:210px;padding:5px;border:1px solid #ddd;margin:2px;min-height:250px;">
    <div>
    <?php if ($imgSrc != ""):?>
    <a href="/<?php echo $imgSrc?>" target="_blank">
        <img class="fkimg" style="width:200px;height:120px;" src="/<?php echo $imgSrc?>" alt="<%php echo $imgDescription ?>" />
    </a>
    <?php else: ?>
	<div>
        <a href="#" target="_blank">
	    <img class="fkimg" style="width:200px;height:120px;" src="/images/img_placeholder.jpg" alt="NO Image available" />
        </a>
	</div>
    <?php endif; ?>
    </div>
    <div>
        <input type="file" name="file_upload" name="file_upload" />
        <input type="hidden" name="file_del" id="file_del" value="" />
    </div>
</div>

Ok niente di speciale fino qui, chiaramente ho impostato il valore della variabile $imgSrc, nel primo caso, mentre nel secondo caso invece l'immagine non c'è (infatti l'immagine può essere già impostata, per esempio se il valore viene letto da un database, e quindi dovremmo fornire un'anteprima dell'immagine attualmente valorizzata)

Abbiamo inserito quindi, nel codice del Listato 1.1, sia l'immagine, e sia il tag di input. E' stato aggiunto un ulteriore tag di input di tipo hidden, che verrà utilizzato per eliminare l'immagine, nel caso già esista e si voglia eliminare, o nel caso si sia scelto un file, ma poi non si vuole più caricare.

Adesso viene il bello, cioè il momento di personalizzare il tutto con jQuery e un pizzico di css. Sì perchè jQuery/css ci permettono di espandere di molto le possibilità dell'html che, di base, sono... scarse diciamocelo...

E' ora di mettere mano alla funzione "ready" dell'oggetto jQuery(document), premettendo di aver inserito nella pagina il link per includere ed utilizzare jquery. In più inseriamo i link per includere bootstrap, il tutto nella sezione head, che sarà più o meno la seguente (a seconda delle viersioni di bootstrap e jquery che sceglierete potrebbero cambiare i link)

Listato 1.2

<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">

<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script
  src="https://code.jquery.com/jquery-3.3.1.min.js"
  integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
  crossorigin="anonymous"></script>
</head>

Alla fine del file, prima della chiusura del body, andremo ad inserire il seguente codice, che poi spiegheremo.

<script>
	function readURL(input)
	{
		if (input.files && input.files[0])
		{
			
			var reader = new FileReader();
			reader.onload = function(e){
				jQuery(input).parent().parent().find('.fkimg').attr("src",e.target.result);
			}
			reader.readAsDataURL(input.files[0]);
		}
	}
	jQuery(document).ready(function()
	{
		jQuery("input[type=file]").css("display",'none');
		jQuery("input[type=file]").parent().append('<div class="fname">&nbsp;</div>');
		jQuery("input[type=file]").parent().append('<button class="btn btn-success choosefile">Scegli file</button>');
		jQuery("input[type=file]").parent().append('<button class="btn btn-warning delfile">Elimina</button>');
		jQuery("input[type=file]").change(function(e){
			var fName = e.target.files[0].name;
			var el = jQuery(e.target).parent();
			jQuery(e.target).parent().find(".fname").html(fName);
			readURL(e.target);
		});
		jQuery(document).on("click",".choosefile", function(event){
			event.preventDefault();
			var o = jQuery(event.target).parent().find('input[type="file"]');
			o.click();
			//alert(o.attr("id"));
		});
		jQuery(document).on("click",".delfile", function(event){
			event.preventDefault();
			var o = jQuery(event.target).parent().find('input[type="hidden"]');
			o.val("cancel");
			var a = jQuery(event.target).parent().parent().find('a');
			var img = jQuery(event.target).parent().parent().find('img');
			a.attr('href','#')
			img.attr('src','/images/img_placeholder.jpg');
			
		});	
	});
</script>

Cominciamo dando un'occhiata alla funzione readURL, a cui passiamo un oggetto jquery di tipo input type="file". Questa funzione non fa altro che caricare nell'immagine di preview, l'immagine che scegliamo quando facciamo click sul tasto Scegli file, che esamineremo a breve. Se volete sapere di più di come si utilizza e cosa è un oggetto FileReader (utilizzato nella funzione readURL), vi rimando a questo articolo, che spiega in maniera chiara il tutto.

Facendo il punto della situazione, non facciamo altro che nascondere (rendendolo invisibile) il tag <input type="file" />, fatto ciò aggiuniamo due pulsanti, uno per selezionare l'immagine (il cui evento click viene "abbinato" all'evento click del tag input file), e uno per eliminare l'immagine, e un tag <div class="fname"> che non sarà altro l'etichetta che conterrà il nome del file selezionato dall'utente.

quindi implementiamo 3 funzioni: evento click sul pulsante "Scegli file", evento click sul pulsante "Elimina file", ed evento change su input file. L'evento change del tag input file è necessario per 2 motivi: 

  1. creare un'anteprima dell'immagine che caricheremo nell'apposito tag img
  2. reperire il nome del file e visualizzarlo nella label creata

L'evento click del pulsante "Scegli file" altro non fa che richiamare l'evento click() del tag input file.

Invece l'evento click del pulsante Elimina file, da un lato cancella l'immagine presente nel tag <img> sostituendolo con un placeholder che indica l'assenza di un'immagine, dall'altro imposta il valore del tag input hidden con classe .delfile a "cancel".

In fase di submit del form, non dobbiamo fare altro che controllare se il valore del parametro post "file_del" è impostato su "cancel", e agire di conseguenza.

Alla fine, avremo una situazione simile a questa: 

 

Se questo articolo vi è piaciuto, o vi è tornato utile, potreste farmi il favore di lasciare un "Mi piace" sulla mia pagina!

Ci vediamo presto con il prossimo articolo Developer!