Mi sono reso conto (quando mi sono visto) che ci sono molte demo e tutorial che ti mostrano come trascinare e rilasciare un file nel browser e quindi renderizzarlo sulla pagina. Sono spesso etichettati come "trascinamento della selezione e caricamento", ma in realtà non vengono caricati. Questa esercitazione ti porterà a quel passaggio finale.
Ti guiderò attraverso un esempio con cui puoi giocare: trascina un file nella pagina Web e verrà caricato sul server all'istante.
Attività #
Suddividiamo questo processo in attività specifiche:
- Acquisire l'evento drop e leggerne i dati.
- Pubblicare tali dati binari nel server.
- Fornire commenti e suggerimenti sullo stato di avanzamento del caricamento.
- Facoltativamente, eseguire il rendering di un'anteprima di ciò che viene caricato e del relativo stato.
Per ottenere tutto questo, sono necessarie le API HTML5 e non HTML5 seguenti:
Tieni presente che non tutti i browser supportano tutta questa tecnologia oggi, ma si stanno avvicinando. Volevo solo creare un tutorial chiaro e completo su come andare l'intero maiale e caricare quel file caduto.
Il risultato finale: siamo in grado di rilasciare il file in qualsiasi punto del browser, otteniamo un'anteprima e un avanzamento del caricamento ed è un'esperienza elegante.

Trascinamento della selezione #
Proprio come un'attenzione, il trascinamento della selezione è noto per essere un po 'un killjoy. In realtà, è un incubo, ma non ripeterò quello che è stato detto prima.
Il nostro esempio ti permetterà di trascinare un file ovunque all'interno del browser. Per raggiungere questo obiettivo, dobbiamo allegare i listener di eventi a
l cor
po o
a documentElement
(ad esempio, il nodo HTML radice). Ascoltando documentEle
ment, questo codi
ce potrebbe essere in qualsiasi punto della pagina,
poiché documentEl
ement esisterà sempre. È possibile ascoltare sul c
orpo
solo una vol
ta<body
>elemento è stato incontrato.</body>
Ecco il modello di codice necessario per acquisire l'evento di rilascio nell'intero documento:
var doc = document.documentElement; doc.ondragover = funzione () { this.className = 'hover';return false; }; doc.ondragend = funzione () { this.className = ''; returnfalse; }; doc.ondrop = funzione (evento) { event.preventDefault && event.preventDefault(); this.className = ''; ora fare qualcosa con: file var = event.dataTransfer.files; restituire false; };
Sto usando la clas
se hov
er in modo da poter attivare o disattivare un suggerimento che spiega all'utente che il file può essere rilasciato sulla pagina.
All'interno
dell'
evento drop, possiamo accedere ai file eliminati tr
amiteevent.dataTransfer.file
s.
Un'alternativa al trascinamento della selezione #
Fastidiosamente, mentre scrivo questo, mi rendo conto che questa combinazione di requisiti API è attualmente soddisfatta solo da Chrome e Firefox. Quindi, facendo il test da Modernizr, possiamo testare il supporto e fornire la nostra alternativa:
var dndSupported = funzione () { var div = document.createElement('div'); return ('draggable' in div) || ('ondragstart' in div & 'ondrop' in div); }; if (!dndSupported()) { // prendere una route alternativa }
Invece di trascinare, possiamo inserire un elemento di input del file (gli ho dato un ID
di
"upload") e quando il suo valore viene modificato, il file può essere raccolto in:
document.getElementById('upload').onchange = function (event){ // 'this' fa riferimento all'elemento generato dall'evento sui file var = this.files; };
È possibile vedere che l'equivalente è semplicem
ente l
a proprietà files d
ell'oggettoHTMLInpu
tElement . Questo ci darà accesso allo stesso file dell'
evento.dataTransfer.fil
es dall'evento drop.
Caricamento automatico del file #
Questo è il pezzo pulito, ed è dolorosamente semplice. Usiamo una funzionalità della specifica XHR
2: FormDat
a. Una volta creata un'istanza di Fo
rmData, è
possibile aggiungerne elementi:
var formData = new FormData(); per (var i = 0; i < files.length; i++) { formData.append('file', files[i]); } // now post a new XHR request var xhr = new XMLHttpRequest(); xhr.open('POST', '/upload'); xhr.onload = function () { if (xhr.status === 200) { console.log('all done: ' + xhr.status); } else { console.log('Something went terribly wrong...'); } }; xhr.send(formData);
di FormData
Sì, è tutto.†
Diamo un'occhiata a un paio di cose chiave che stanno succedendo sul codice sopra.
formData.append('file', file[i]);
Stiamo inviando parametri denominati al nostro server, in particolare una matrice di valori chiamata
fil
e. Ovviamente, puoi chiamarlo come vuoi, ma il fi
le è
il nome che il tuo server starà cercando quando salva il file caricato (o i file).
xhr.onload = funzione () { if (xhr.status === 200) { console.log('tutto fatto: ' + xhr.status); } else { console.log('Qualcosa è andato terribilmente storto...'); } };
Se hai familiarità con XHR, noterai che non stiamo ascoltandoonr
eadystatechange, s
olo onl
oad ,
che è (in effetti) una funzione di convenienza per farvi sapere quando
readyState
è 4 (cioè caricato!). È comunque necessario controllare e rispondere in modo appropriato al codice di stato della richiesta per assicurarsi che sia 200 OK, anziché 500 (errore interno del server) o 404 (non trovato) o qualsiasi altra cosa.
xhr.send(formData);
Il bel trucco qui è che XHR ha impostato automaticamente la codifica dei dati pubblicati su pi
ù parti / dati modul
o. Questa codifica consente al server di leggere e salvare i file. È come la codifica utilizzata quando invii un allegato in un'e-mail.
Fornire feedback all'utente #
XHR2 ora (flippin' finally) viene fornito con un ev
ento di ava
nzamento. Quindi, se stai inviando o recuperando un file di grandi dimensioni tramite XHR, puoi dire all'utente quanto sei lontano.
È piuttosto semplice. Se si desidera tenere traccia dello stato di avanzamento della richiesta XHR, ascoltar
e l'evento
di stato. Un gotcha che mi ha colto per qualche tempo: avevo bisogno di tenere traccia dell'avanzamento del caricamento, non del download. Per fare questo correttamente, è necessario ascoltare i progressi sull'oggetto di caricamentoXHR, in questo modo:
xhr.upload.onprogress = funzione (evento) { if (event.lengthComputable) { var complete = (event.loaded / event.total * 100 | 0); progress.value = progress.innerHTML = complete; } }; xhr.onload = funzione () { // nel caso in cui rimaniamo bloccati intorno al 99% progress.value = progress.innerHTML = 100; };
anzamento
di XHR2Si noti che invece d
i xhr.onprogr
ess, use
xhr.upload.onprogess
. Quando questo evento viene generato, controlliamo che l'evento supporti il calcolo della quantità di dati cari
cati (la parteleng
thComputable), quindi calcoliamo l'importo completato.
Nel mio HTML, sto usando un e
lemento <pro
gress>(simile all'elemento,<m
eter> ma
più semanticamente appropriato mentre presentiamo lo stato di avanzamento di un'attività) per mostrare all'utente quanta parte del loro file è stata caricata:</meter> </progress>
<progress id="progress" min="0" max="100" value="0">0</progress>
Si noti che sto usando i
nnerHTML pe
r browser che non supportano ancora<progres
s>.</progres
s> Quindi, con un pizzico di contenuti generati da CSS, sia th
einnerHTML
che i
l valo
re del progresso possono essere gli stessi (forse un'ottimizzazione troppo, ma all'epoca ero piuttosto orgoglioso di me stesso!).
Infine, rendering di un'anteprima #
Come bella aggiunta, daremo all'utente un'anteprima di ciò che stiamo caricando per loro. Richiede l'API file, in particolare l'A
PI FileRe
ader.
Poiché l'operazione di trascinamento della
selezi[type=file]
one (o l'input) contiene un oggetto file, possiamo consegnarlo
a FileRead
er per ottenere il contenuto del file. Se è qualcosa come un'immagine, possiamo ottenere i dati di Base64 e dare all'utente un'anteprima. O se si tratta di testo, potremmo renderizzarlo in un<di
v>.</di
v> Il tipo MIME per il file è disponibile come pr
opri
età thetype nell'oggetto file.
Quindi supponiamo di accettare solo immagini. Ecco la parte di anteprima che viene eseguita dopo che il file è stato ricevuto dal browser:
var acceptedTypes = { 'image/png': true, 'image/jpeg': true, 'image/gif': true }; if (acceptedTypes === t[file.type]rue) { var reader = new FileReader(); reader.onload = funzione (evento) { immagine var = nuova immagine(); image.src = event.target.result; image.width = 100; un falso ridimensionamento document.body.appendChild(image); }; reader.readAsDataURL(file); }
Creiamo il Fil
eReader e
gli diamo un file da leggere. Nell'esempio precedente, ho usato
readAsDataURL
, ma puoi anche leggere file in formato testo normale e binario (secondo la specifica).
Quando il lettore ha letto il file e i dati sono disponibili, genera l'evento
load,
che a sua volta crea la nostra nuova immagine.
Il risultato dell'azione di lettura del file è i
n event.target.resul
tproperty. Nel caso di readA
sDataURL, i
l valore è lungo le linee di da
ta:image/png;base64,ABC...
.
Utilizzo di tutti gli strumenti nel capannone #
Questo esempio potrebbe essere un po 'artificioso per l'articolo che volevo scrivere. Ho usato molte tecnologie diverse per fare qualcosa che è un modello abbastanza comune sul web. In effetti, è stato perché ho avuto problemi a trovare una fonte definitiva sul caricamento sul server effettivo (tranne, ovviamente, come ho scritto questo articolo ho trovato questo esempio dalla fine del 2010!).
Spero che vedrai come ogni bit di tecnologia può lavorare insieme per creare un'applicazione, ma allo stesso modo spero che tu possa vedere dove funzionerebbe se nessuno o solo parte della tecnologia non fosse disponibile. Assicurarsi di rilevare la funzionalità. Assicurati di fornire fallback. Assicurati di sviluppare responsabilmente.
Ecco la demo finale di trascinamento della selezione e caricamento con cui giocare.