HTML5_FILE_API - Drag and Drop di file.
§ 1. L'oggetto DataTransfer durante il trascinamento di file
Nell'esempio precedente abbiamo visto come l'operazione di trascinamento generi un oggetto DataTranfer contenente informazioni sui dati trascinati. Vediamo cosa succede quando ad essere trascinati sono uno o più file selezionati mediante una finestra aperta tramite un elemento di input:
<input type="file" multiple>
:
Non appena iniziamo a trascinare un file all'interno, o al di fuori della finestra di selezione, ci rendiamo conto che potremmo rilasciarlo dovunque:
- all'interno della finestra di selezione. Ad esempio sopra l'immagine di una qualsiasi cartella, provocando così lo spostamento del file selezionato nella cartella di destinazione;
- sul desktop;
- in una qualsiasi posizione della pagina web contente il campo di input da cui abbiamo avviato il processo;
- in una qualsiasi altra pagina web. Ottenendo un effetto che dipende dal tipo di file selezionato e da come è configurata la pagina web di destinazione.
In particolare il rilascio sulla pagina web stessa può provocare malfunzionamenti. Qui sono evitati tramite questo script:
var dropzoneId = 'dropzone';
window.addEventListener('dragenter', function(e) {
if (e.target.id != dropzoneId) {
e.preventDefault();
e.dataTransfer.dropEffect = "none";
console.log('window.dragenter');
}
},false);
window.addEventListener('dragover', function(e) {
if (e.target.id != dropzoneId) {
e.preventDefault();
e.dataTransfer.dropEffect = "none";
console.log('window.dragover ');
}
});
window.addEventListener('drop', function(e) {
if (e.target.id != dropzoneId) {
e.preventDefault();
e.dataTransfer.dropEffect = "none";
console.log('window.drop');
}
});
In pratica sono disabilitati tutti gli effetti di trascinamento e rilascio su tutti gli elementi della finestra ad eccezione di quello con id=dropzone , l'area gialla sottostante.
Il comportamento di default di un file trascinato sopra un elemento è quello di copiare il file nella locazione di destinazione. Per evitare questo comportamento ho utilizzato il seguente frammento di codice:
document.getElementById('dropzone').addEventListener('drop', Drop, false);
document.getElementById('dropzone').addEventListener('dragover', DragOver, false);
function DragOver(e){
console.log("DragOver");
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect='link';
return;
}
function Drop(e){
console.log("Drop");
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect="none";
...
...
e.stopPropagation();
e.preventDefault();
return;
}
Arrivati a questo punto possiamo utilizzare l'event listener "Drop" per vedere cosa c'è nell'oggetto DataTransfer.
Visualizzeremo tutte e properties dell'oggetto DataTransfer caricate selezionando i file, trascinandoli e rilasciandoli in dropzone:
- types - E' una array di stringhe con il formati settati nell'evento dragstart;
- dropEffect - Indica il tipo di operazione di drag-and-drop correntemente settata. Il suo valore può essere: none, copy, link, move;
- effectAllowed - Indica il tipo di operazioni possibili. Una fra: none, copy, copyLink, copyMove, link, linkMove, all, o uninitialized;
- files - Contiene una lista di tutti i file locali disponibili nell'oggetto DataTransfer, Se l'operazione di drag non riguarda files, questa proprietà sarà una lista vuota;
- items - è un oggetto DataTransferItemList che consiste in una lista di tutti i dati trascinati.
E questo è il resto della funzione Drop:
function Drop(e){
console.log("Drop");
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect="none";
var prop = [];
prop.push('<p>e.dataTransfer.types : ' + e.dataTransfer.types + '</p>');
prop.push('<p>e.dataTransfer.effectAllowed : ' + e.dataTransfer.effectAllowed + '</p>');
prop.push('<p>e.dataTransfer.dropEffect : ' + e.dataTransfer.dropEffect + '</p>');
prop.push('<p>e.dataTransfer.files : ' + e.dataTransfer.files + '</p>');
prop.push('<p>e.dataTransfer.files.length : ' + e.dataTransfer.files.length + '</p>');
prop.push('<p>e.dataTransfer.items : ' + e.dataTransfer.items + '</p>');
prop.push('<p>e.dataTransfer.items.length : ' + e.dataTransfer.items.length + '</p>');
document.getElementById('text_output_1').innerHTML = '<p>Properties dell oggetto DataTransfer trascinato in dropzone:</p>' + prop.join('');
e.stopPropagation();
e.preventDefault();
return;
}
§ 2. Esplorare gli oggetti File
La property files di DataTransfer contiene un array di oggetti File ognuno con le seguenti properties:
- name
- type
- size
- lastModified
- lastModifiedDate
- webkitRelativePath
E questo è il codice :
document.getElementById('dropzone2').addEventListener('drop', Drop2, false);
document.getElementById('dropzone2').addEventListener('dragover', DragOver2, false);
function DragOver2(e){
console.log("DragOver2");
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect='link';
return;
}
function Drop2(e){
console.log("Drop2");
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect="none";
var prop = [];
var dati = e.dataTransfer;
if (dati.files.length>0){
for (var i=0; i < dati.files.length; i++){
prop.push('<p>file: ' + dati.files[i].name + '</p><ul>');
prop.push('<li>type: ' + dati.files[i].type + '</li>');
prop.push('<li>size: ' + dati.files[i].size + '</li>');
prop.push('<li>lastModified: ' + dati.files[i].lastModified + '</li>');
prop.push('<li>lastModifiedDate: ' + dati.files[i].lastModifiedDate + '</li>');
prop.push('<li>webkitRelativePath: ' + dati.files[i].webkitRelativePath + '</li></ul>');
}
document.getElementById('text_output_2').innerHTML = '<p>Properties degli oggetti file</p>' + prop.join('');
}else{
alert("Nessun file selezionato");
}
e.stopPropagation();
e.preventDefault();
return;
}
§ 3. Utilizzo di FileReader
L'oggetto FileReader permette alle applicazioni web di leggere in maniera asincrona il contenuto di file (o di buffer di dati) memorizzati nel computer dell'utente, usando gli oggetti File o Blob per specificare i dati da leggere.
Gli oggetti file possono essere restituiti dagli oggetti FileList in seguito alla selezione di file operata mediante un elemento html input come visto sopra, oppure da un oggetto DataDransfer in seguito ad un operazione di drag and drop.
CostruttorePer creare un oggetto FileReader usare la seguente sintassi:
var reader= new FileReader();
Eventi
Un oggetto FileReader può generare i seguenti eventi:
- abort, ogni volta che una operazione di lettura viene abortita;
- error, ogni volta che una operazione di lettura incontra un errore;
- load, ogni volta che una operazione di lettura viene completata con successo;
- loadstart, ogni volta che ha inizio una lettura
- loadend, ogni volta che una operazione di lettura termina sia con successo che con fallimento.
- onprogress, durante l'operazione di lettura di un Blob
Un oggetto FileReader possiede le seguenti properties:
- error. Sola lettura. Una DOMException che rappresenta l'errore incontrato durante la lettura del file;
- readyState. Sola lettura. Un numero che indica lo stato del FileReader:
- EMPTY - 0 Nessun dato ancora caricato.
- LOADING - 1 Il dati sono in fase di caricamento
- DONE - 2 Lettura completata
- result Sola lettura. I contenuti del file. Questa proprietà è valida solo dopo che l'operazione di lettura è stata completata, e il formato dei dati dipende da quale metodo è stato usato per iniziare l'operazione di lettura.
Un oggetto FileReader possiede i seguenti metodi:
- abort
Sintassi - instanceOfFileReader.abort();
Provoca l'abort dell'operazione di lettura. Al ritorno, lo stato della proprietà readyState sarà DONE; - readAsArrayBuffer
Sintassi - instanceOfFileReader.readAsArrayBuffer(blob);
Legge il contenuto del blob/file specificato sotto forma di una ArrayBuffer; - readAsBinaryString
Sintassi - instanceOfFileReader.readAsBinaryString(blob);
Legge il contenuto del blob/file specificato sotto forma di una stringa di dati binari. Questo metodo è stato mantenuto per motivi di compatibilità delle versioni precedenti. Nelle nuove applicazioni usare readAsArrayBuffer. - readAsDataURL
Sintassi - instanceOfFileReader.readAsDataURL(blob);
Legge il contenuto del blob/file specificato restituendo in result i dati del file in forma di URL codificato come una stringa base64. - readAsText
Sintassi - instanceOfFileReader.readAsText(blob[, encoding]);
Legge il contenuto del blob/file specificato sotto forma di una stringa di testo, codificata in base al valore del parametro opzionale encoding. Il valore di default di encoding è UTF-8.
Utiliziamo questo metodo per
§ 3.1. Utilizzo del metodo readAsText
Limitiamo l'accesso del campo di input ai soli file elencati nell'attributo accept del campo di input.
<input type="file" accept=".txt,.css,.js,.dat,.html,.xml," >
:
Il trascinamento e rilascio del file in "dropzone3" provocherà l'esecuzione del metodo readAsText del file, e la visualizzazione del suo contenuto nella zona sottostante.
Ecco il codice :
var reader = new FileReader();
reader.addEventListener('load', suEventoLoad);
var f;
document.getElementById('dropzone3').addEventListener('drop', Drop3);
document.getElementById('dropzone3').addEventListener('dragover', DragOver3);
function DragOver3(e){
console.log("DragOver3");
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect='link';
}
function Drop3(e){
console.log("Drop3");
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect="none";
if (e.dataTransfer.files.length > 0 && (e.dataTransfer.types.includes("Files"))){
alert ("Selezionato file: " + e.dataTransfer.files[0].name);
f=e.dataTransfer.files[0];
if (f.type.match('image.*')){
reader.readAsDataURL(f);
}else if(f.type.match('text.*')){
reader.readAsText(f,'UTF 8');
}else{
alert ("Questa applicazione non consente di visualizzare file di questo tipo: " + f.type);
}
}else{
alert("Nessun file selezionato");
}
e.stopPropagation();
e.preventDefault();
}
function suEventoLoad(e){
if (f.type.match('text.*')){
document.getElementById('text_output_3').innerHTML = e.target.result.replace(/(?:\r\n|\r|\n)/g, '<br /'>');
} else if(f.type.match('image.*')){
//alert ("selezionata una immagine");
document.getElementById('img_t').src = e.target.result;
}
}
§ 3.2. Utilizzo del metodo readAsDataURL
Limitiamo l'accesso del campo di input ai soli file image.
<input type="file" accept=image/* >
:
Il trascinamento e rilascio del file in "dropzone4" provocherà l'esecuzione del metodo readAsDataURL del file, e la sostituzione di "Image preview..."
Ecco il codice html con il quale è stata definita "Image preview...":
<img id="img_t" alt=" Image preview...." src='' style="width:80%";>
E questo è il codice JavaScript
var reader = new FileReader();
reader.addEventListener('load', suEventoLoad);
var f;
document.getElementById('dropzone4').addEventListener('drop', Drop4);
document.getElementById('dropzone4').addEventListener('dragover', DragOver4);
function DragOver4(e){
console.log("DragOver3");
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect='link';
}
function Drop4(e){
console.log("Drop3");
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect="none";
if (e.dataTransfer.files.length > 0 && (e.dataTransfer.types.includes("Files"))){
f=e.dataTransfer.files[0];
if (f.type.match('image.*')){
reader.readAsDataURL(f);
}else if(f.type.match('text.*')){
reader.readAsText(f,'UTF 8');
}else{
alert ("Questa applicazione non consente di visualizzare file di questo tipo: " + f.type);
}
}else{
alert("Nessun file selezionato");
}
e.stopPropagation();
e.preventDefault();
}
function suEventoLoad(e){
if (f.type.match('text.*')){
document.getElementById('text_output_3').innerHTML = e.target.result.replace(/(?:\r\n|\r|\n)/g, '<br /'>'');
} else if(f.type.match('image.*')){
//alert ("selezionata una immagine");
document.getElementById('img_t').src = e.target.result;
}
}