Mar 212013
 

Probabilmente una cosa che molti sviluppatori hanno sempre sognato di fare è l’upload dei files sul server trascinandoli direttamente dal proprio desktop, o dal file system più in generale, nel browser. Ecco con html5 questa cosa finalemnte si potrà fare, vediamo come.

Partiamo dall’html:


<section id="wrapper">
<header><h1>Drag and drop, upload automatico</h1></header>
<article>
<div id="holder"></div>
<p id="upload"><label>Drag &amp; drop non supportato, procedi attraverso il campo di input:<br><input type="file"></label></p>
<p id="filereader">File API &amp; FileReader API non supportato</p>
<p id="formdata">XHR2 FormData non supportato</p>
<p id="progress">XHR2 upload progress non supportato</p>
<p>Upload progress: <progress id="uploadprogress" min="0" max="100" value="0">0</progress></p>
<p>Trascina un'immagine dal tuo desktop nella drop zone, vedrai nel browser il render della preview, ma verrà anche automaticamente fatto l'upload sul server.</p>
</article>
</section>

 

Oltre al titolo abbiamo il div id=”holder” che sarà l’aea di rilascio dell’oggetto; quattro paragarafi contenenti i messaggi di errore relativi a funzionalità html5 che potrebbero non essere supportate da tutti i browser, il tag  progress per la gestione della progress bar relativa all’upload e un paragrafo di istruzioni.

Rapidamente anche il css, o almeno una parte:


#holder { border: 10px dashed #ccc; width: 300px; min-height: 300px; margin: 20px auto;}
#holder.hover { border: 10px dashed #0c0; }
#holder img { display: block; margin: 10px auto; }
#holder p { margin: 10px; font-size: 14px; }
progress { width: 100%; }
progress:after { content: '%'; }
.fail { background: #c00; padding: 2px; color: #fff; }
.hidden { display: none !important;}

Abbastanza importante la formattazione di #holder che deve rendere facilmente riconoscibile la drop zone,

per il resto da notare la classe .hidden per nascondere le funzionalità supportate.

Ma veniamo al cuore javascript del nostro lavoro e iniziamo con la definizione delle variabili che ci serviranno:


var holder = document.getElementById('holder'),
tests = {
filereader: typeof FileReader != 'undefined',
dnd: 'draggable' in document.createElement('span'),
formdata: !!window.FormData,
progress: "upload" in new XMLHttpRequest
},
support = {
filereader: document.getElementById('filereader'),
formdata: document.getElementById('formdata'),
progress: document.getElementById('progress')
},
acceptedTypes = {
'image/png': true,
'image/jpeg': true,
'image/gif': true
},
progress = document.getElementById('uploadprogress'),
fileupload = document.getElementById('upload');

 

Oltre ai document.getElementById ci interessa la definizione dei test che dovremo attuare prima di poter procedere cioè relativi al supporto delle API FileReader, Drag&Drop, FormData e del tag progress e dei tipi di dato, immagini, accettate dal nostro uploader. Aquesto punto possiamo eseguire il test:


"filereader formdata progress".split(' ').forEach(function (api) {
if (tests[api] === false) {support[api].className = 'fail';}
else {support[api].className = 'hidden'; }
});

Mettiamo i test da compiere in una stringa, la splittiamo creando un’array sul quale fare un ciclo foreach, se il test fallisce applichiamo la classe css fail per evidenziare il paragrafo, viceversa lo nascondiamo. Aquesto punto se il nostro browser supporta le funzionalità necessarie possiamo procedere. O meglio attuiamo l’ultimo test sul drag&drop e gestiamo il tutto:


if (tests.dnd) {
holder.ondragover = function () { this.className = 'hover'; return false; };
holder.ondragend = function () { this.className = ''; return false; };
holder.ondrop = function (e) {
this.className = '';
e.preventDefault();
readfiles(e.dataTransfer.files);
}
}
else {
fileupload.className = 'hidden';
fileupload.querySelector('input').onchange = function () {
readfiles(this.files);
};
}


Ovvero se il test fallisce passiamo alla modalità input tradizionale, altrimenti associamo agli eventi dragover e dragend le opprtune classi css prima definite, ma soprattutto all’ondrop chiamiamo la funzione readfiles() assando come argomento il file caricato. La funzione readiles dovrà fare 3 cose: mostrare il file nella preview e di questo si occuperà un’apposita funzione, passare il file al server e infine gestire la progress bar.

Procediamo in ordine e iniziamo dalla preview:

    var formData = tests.formdata ? new FormData() : null;
for (var i = 0; i < files.length; i++) {
if (tests.formdata) formData.append('file', files[i]);
previewfile(files[i]);
}

function previewfile(file) {
if (tests.filereader === true && acceptedTypes[file.type] === true) {
var reader = new FileReader();
reader.onload = function (event) {
var image = new Image();
image.src = event.target.result;
image.width = 250; // a fake resize
holder.appendChild(image);
};
reader.readAsDataURL(file);
}
else {
holder.innerHTML += '<p>Uploaded ' + file.name + ' ' + (file.size ? (file.size/1024|0) + 'K' : '');
console.log(file);
}
}

Nel caso specifico quindi la funzione si occupa di testare il supporto dell’API, che l’immagine sia fra quelle accettate e infine di “appenderla” nella pagina a una larghezza fissa di 250px
Il secondo step è costituito dall’upload del file tramite ajax

if (tests.formdata) {
var xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.php');
xhr.onload = function() {
progress.value = progress.innerHTML = 100;
};

Un po’ vecchia maniera!. L’upload php si occuperà della gestione server side del file caricato, per esempio così:


$upload_dir=$_SERVER['DOCUMENT_ROOT'].'/upload/';
$filename=$_FILES['file']['name'];
if(is_uploaded_file($_FILES['file']['tmp_name'])){
move_uploaded_file($_FILES['file']['tmp_name'],$upload_dir.$filename) or die($upload_dir.$filename);
}

Il passaggio precedente si completa e integra con l’invio della chiamata e la sincronizzazione della progress bar:

    if (tests.progress) {
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
var complete = (event.loaded / event.total * 100 | 0);
progress.value = progress.innerHTML = complete;
}
}
}
xhr.send(formData);
}

L’esempio

Feb 032013
 

Siamo oggi di fronte all’annosa questione: come fare l’apload di un file usando un sisstema un po’ moderno e intelligente ma facilemente integrabile nei binari gà noti della programmazione php.

La soluzione proporsta prevede l’uso del celebre plugin di jQuery uploadify

Da questo sito scaricheremo i files che ci servono dalla pagina di download, ovvero il plugin, il suo css, un file swf per la realizzazione della prgogress bar e, già che ci siamo, il file php per spostare il file nella cartella di destinazione finale. Il nostro scopo sarà consentire all’utente la scelta e l’upload del file prima e indipendetemente dall’invio del form, gestiremo la risposta per sapere il nome del file caricato.

Partiamo quindi dalle intestazioni del file html:


<link rel="stylesheet" type="text/css" href="uploadify.css" />
<script src="http://code.jquery.com/jquery-1.9.0.min.js"></script>
<script src="js/jquery.uploadify.min.js"></script>

Proseguiamo con il form:


<form>
<fieldset>
<legend>File upload</legend>
<label for="file_upload_1">File</label><br>
<input type="hidden" id="file_upload" name="file_upload">
<input type="file" id="file_upload_1" name="file_upload_1"><br><br>
<input type="submit">
</fieldset>
</form>


Useremo il campo nascosto per memorizzare il nome del file così come verrà salvato.

Per attivare il controllo sull’input file procederemo in questo modo:


$(function() {
$("#file_upload_1").uploadify({
height        : 30,
swf           : 'uploadify.swf',
uploader      : 'uploadify.php',
width         : 120,
onUploadSuccess : function(file, data, response) {$('#file_upload').val(data);}
});
});

La maggiorn parte delle informazioni dovrebbero essere evidenti, la parte forse pià interessante è l’onUploadSuccess che scrive nell’input hidden, per il resto sono i percorsi dei due file scaricati.

Per quanto rigarda il file php, il codice è il seguene e l’unica personalizzazione riguarda il path di destinazione dei files, poi lo si potrà personalizzare e integrare con gli opportuni controlli come meglio si crede:


$targetFolder = '/blog/tutorial/upload'; // Relative to the root
if (!empty($_FILES)) {
$tempFile = $_FILES['Filedata']['tmp_name'];
$targetPath = $_SERVER['DOCUMENT_ROOT'] . $targetFolder;
$targetFile = rtrim($targetPath,'/') . '/' . $_FILES['Filedata']['name'];

// Validate the file type
$fileTypes = array('jpg','jpeg','gif','png'); // File extensions
$fileParts = pathinfo($_FILES['Filedata']['name']);

if (in_array($fileParts['extension'],$fileTypes)) {
move_uploaded_file($tempFile,$targetFile);
echo $_FILES['Filedata']['name'];
} else {
echo 'Invalid file type.';
}
}

L’esempio