Slik oppretter du en Resumable Video Uploader i Node.js

Hvis du noen gang har lastet opp en betydelig stor videofil, vet du denne følelsen: du er 90% ferdig, og oppdatere siden ved et uhell - å måtte begynne igjen.

I denne opplæringen vil jeg demonstrere hvordan du lager en videoopplasting til nettstedet ditt som kan gjenoppta en avbrutt opplasting, og generere en miniatyrbilde ved ferdigstillelse.


Intro

For at denne opplasteren skal kunne gjenopptas, må serveren holde oversikt over hvor mye en fil allerede er lastet opp, og kunne fortsette fra hvor den sluttet. For å oppnå denne oppgaven vil vi gi full kontroll til Node.js-serveren for å be om bestemte datablokker, og HTML-skjemaet vil hente disse forespørslene og sende den nødvendige informasjonen til serveren.

For å håndtere denne kommunikasjonen bruker vi Socket.io. Hvis du aldri har hørt om Socket.io, er det et rammeverk for sanntids kommunikasjon mellom Node.js og en HTML-nettside - brøt deg mer inn i dette snart.

Dette er det grunnleggende konseptet; Vi starter med HTML-skjemaet.


Trinn 1: HTML

Jeg skal holde HTML ganske enkelt; alt vi trenger er et innspill for å velge en fil, en tekstboks for navnet og en knapp for å starte opplastingen. Her er den nødvendige koden:

  

Videoopplasting



Legg merke til at jeg har pakket inn innholdet i et spekter; Vi vil bruke dette senere for å oppdatere sidens layout med JavaScript. Jeg kommer ikke til å dekke CSS i denne opplæringen, men du kan laste ned kildekoden hvis du vil bruke minen.


Trinn 2: Gjør det til jobb

HTML5 er fortsatt relativt nytt, og er ikke fullt støttet i alle nettlesere. Det første vi må gjøre, før du går videre, er at brukerens nettleser støtter HTML5-fil API og FileReader-klassen.

FileReader-klassen lar oss åpne og lese deler av en fil og sende dataene som en binær streng til serveren. Her er JavaScript for funksjonen gjenkjenning:

 window.addEventListener ("load", Klar); funksjon Klar () hvis (window.File && window.FileReader) // Dette er de relevante HTML5 objektene som vi skal bruke document.getElementById ('UploadButton'). addEventListener ('klikk', StartUpload); document.getElementById ('FileBox'). addEventListener ('change', FileChosen);  else document.getElementById ('UploadArea'). innerHTML = "Nettleseren din støtter ikke fil-APIen Oppdater din nettleser"; 

Koden ovenfor legger til tilleggshendlere til knappen og filinngangen i skjemaet. De FileChosen Funksjonen setter bare en global variabel med filen - slik at vi får tilgang til den senere - og fyller inn navnefeltet, slik at brukeren har et referansepunkt når navnet navngis. Her er FileChosen funksjon:

 var valgt fil; funksjon FileChosen (evnt) SelectedFile = evnt.target.files [0]; document.getElementById ('NameBox'). value = SelectedFile.name; 

Før vi skriver StartUpload funksjon, vi må sette opp Node.js serveren med socket.io; la oss ta vare på det nå.


Trinn 3: Socket.io Server

Som nevnt tidligere bruker jeg Socket.io for kommunikasjon mellom serveren og HTML-filen. For å laste ned Socket.io, skriv inn npm installere socket.io inn i et terminalvindu (forutsatt at du har installert Node.js), når du har navigert til denne prosjektkatalogen. Slik fungerer socket.io: enten serveren eller klienten "sender ut" en hendelse, og den andre siden vil hente denne hendelsen i form av en funksjon med mulighet for å sende JSON-data frem og tilbake. For å komme i gang, opprett en tom JavaScript-fil, og legg inn følgende kode i den.

 var (app), fs = krever ('fs'), exec = krever ('child_process'). exec , util = kreve ('util') app.listen (8080); funksjonshåndterer (req, res) fs.readFile (__ dirname + '/index.html', funksjon (feil, data) if (err) res.writeHead (500); return res.end ('Feil laster indeks. html '); res.writeHead (200); res.end (data););  io.sockets.on ('connection', funksjon (socket) // Hendelser vil gå her);

De fem første linjene inkluderer de nødvendige bibliotekene, neste linje instruerer serveren til å lytte på port 8080, og håndteringsfunksjonen sender rett og slett innholdet i vår HTML-fil til brukeren når han åpner nettstedet.

De to siste linjene er socket.io-håndterer og vil bli kalt når noen forbinder, via Socket.io.

Nå kan vi gå tilbake til HTML-filen og definere noen socket.io-hendelser.


Trinn 4: Noen Socket.io Hendelser

For å begynne å bruke Socket.io på vår side, må vi først koble til sitt JavaScript-bibliotek. Du gjør dette på samme måte som du vil referere til et bibliotek: referer det til i hovedområdet. Legg til følgende på siden, før skriptene dine, åpenbart.

Ikke bekymre deg for å få denne filen, da den genereres på kjøretid av Node.js-serveren.

Nå kan vi skrive StartUpload funksjon som vi koblet til vår knapp:

 var socket = io.connect ('http: // localhost: 8080'); var FReader; var navn; funksjon StartUpload () if (document.getElementById ('FileBox'). verdi! = "") FReader = new FileReader (); Name = document.getElementById ('NameBox'). Verdi; var innhold = "Laster opp "+ SelectedFile.name +" som "+ Name +""; Innhold + = '
0%'; Innhold + = " - 0/ "+ Math.round (SelectedFile.size / 1048576) +" MB"; document.getElementById ('UploadArea'). innerHTML = Innhold; FReader.onload = funksjon (evnt) socket.emit ('Last opp', 'Navn': Navn, Data: evnt.target.result); socket.emit ('Start', 'Name': Name, 'Size': SelectedFile.size); else alert ("Vennligst velg en fil");

Den første linjen kobles til Socket.io-serveren; neste, vi har opprettet to variabler for filleseren og navnet på filen, da vi trenger global tilgang til disse. Innenfor funksjonen sørget vi først for at brukeren valgte en fil, og hvis de gjorde det, oppretter vi Filereader, og oppdater DOM med en fin fremdriftslinje.

FileReaderens på Last Metoden kalles hver gang den leser noen data; alt vi trenger å gjøre er å avgi en Laste opp hendelse, og send dataene til serveren. Til slutt sender vi ut en Start hendelse, passerer i filens navn og størrelse til Node.js-serveren.

La oss nå gå tilbake til Node.js-filen, og implementere håndtere for disse to hendelsene.


Trinn 5: Håndtering av hendelsene

Du må fjerne bufferen hver så ofte, eller serveren vil krasje på grunn av minneoverbelastning.

Socket.io-hendelsene går inn i handleren som vi har på den siste linjen i vår Node.js-fil. Den første hendelsen som vi skal implementere er Start hendelse som utløses når brukeren klikker på Laste opp knapp.

Jeg nevnte tidligere at serveren burde ha kontroll over hvilke data den vil motta neste; Dette vil tillate det å fortsette fra en tidligere opplasting som var ufullstendig. Det gjør dette ved først å avgjøre om det var en fil med dette navnet som ikke fullførte opplasting, og i så fall vil det fortsette fra hvor det gikk av; ellers begynner det i begynnelsen. Vi overfører disse dataene i halv megabyte trinn, som kommer ut til 524288 byte.

For å holde oversikt over ulike opplastinger som skjer samtidig, må vi legge til en variabel for å lagre alt. Til toppen av filen legger du til var Files = ; ' Her er koden for Start begivenhet:

 socket.on ('Start', funksjon (data) // data inneholder variablene vi passerte gjennom i HTML-filen var Name = data ['Name']; Files [Name] = // Opprett en ny oppføring i Filen Variabel FileSize: data ['Size'], Data: "", Nedlastet: 0 var Sted = 0; prøv var Stat = fs.statSync ('Temp /' + Navn) ) Files [Name] ['Downloaded'] = Stat.size; Plass = Stat.size / 524288; catch (er)  // Det er en ny fil fs.open ("Temp /" + Name, " a, 0755, funksjon (feil, fd) hvis (err) console.log (err); else Filer [Navn] ['Handler'] = fd; // Vi lagrer filbehandleren slik at vi kan skrive til det senere socket.emit ('MoreData', 'Place': Place, Percent: 0);;;);

Først legger vi til den nye filen til filer array, med størrelsen, data og mengden av byte som er lastet ned så langt. De Sted variable butikker hvor i filen vi er opp til - det er standard 0, som er begynnelsen. Vi kontrollerer deretter om filen allerede eksisterer (dvs. den var i midten og stoppet), og oppdater variablene tilsvarende. Enten det er en ny opplastning eller ikke, åpner vi filen for å skrive til Temp / mappe og avgir MoreData hendelse for å be om neste del av data fra HTML-filen.

Nå må vi legge til Laste opp Event, som, hvis du husker, blir kalt hver gang en ny blokk av data blir lest. Her er funksjonen:

 socket.on ('Last opp', funksjon (data) var Navn = data ['Navn']; Filer [Navn] ['Nedlastet'] + = Data ['Data']. Lengde; Filer [Navn] ['Data '] + = data [' Data ']; hvis (Filer [Navn] [' Nedlastet '] == Filer [Navn] [' Filstørrelse ']) // Hvis filen er fullstendig lastet opp fs.write (Files [Name] ['Handler'], Filer [Navn] ['Data'], null, 'Binary', funksjon (feil, Writen) // Få miniatyrbilde her); else if (Files [Name] ['Data'] .length> 10485760) // Hvis databufferen når 10MB fs.write (Filer [Navn] ['Handler'], Filer [Navn] ['Data'], null, 'Binær', funksjon (feil, Writen) Filer [Navn] ['Data'] = ""; // Tilbakestill bufferen var Sted = Filer [Navn] ['Nedlastet'] / 524288; Var Prosent = (Filer [Navn] ['Nedlastet'] / Filer [ Navn] ['FileSize']) * 100; socket.emit ('MoreData', 'Sted': Sted, 'Prosent': Prosent);); else var Place = Files [Name] ['Downloaded '] / 524288; var Prosent = (Filer [Navn] [' Nedlastet '] / Filer [Navn] [' Filstørrelse ']) * 100; socket.emit (' MoreData ', ' Sted ': Sted,' Prosent ' : Prosent););

De to første linjene i denne koden oppdaterer bufferen med de nye dataene, og oppdaterer den totale bytes nedlastede variabelen. Vi må lagre dataene i en buffer og lagre den i trinn, slik at det ikke krasjer serveren på grunn av minneoverbelastning; hver ti megabyte lagrer vi bufferen.

Den første hvis setningen bestemmer om filen er fullstendig oppladet, den andre kontrollerer om bufferen har nådd 10 MB, og til slutt ber vi om MoreData, passerer i prosent ferdig og neste blokk med data for å hente.

Nå kan vi gå tilbake til HTML-filen og implementere MoreData arrangement og oppdatere fremdriften.


Trinn 6: Å holde oversikt over fremdriften

Jeg opprettet en funksjon for å oppdatere fremdriftslinjen og mengden MB lastet opp på siden. I tillegg til det Flere data Hendelsen leser databasen som serveren ba om, og sender den videre til serveren.

For å dele filen i blokker, bruker vi fil-API-ene Skjære kommando. Siden File API er fortsatt i utvikling, må vi bruke webkitSlice og mozSlice for henholdsvis Webkit og Mozilla-nettlesere.

 socket.on ('MoreData', funksjon (data) UpdateBar (data ['Prosent']); var Sted = data ['Place'] * 524288; // Startblokken for neste blokk var var NewFile; // Variabel vil holde den nye databasen hvis (SelectedFile.webkitSlice) NewFile = SelectedFile.webkitSlice (Sted, Sted + Math.min (524288, (SelectedFile.size-Place)); ellers NewFile = SelectedFile.mozSlice (Sted, sted + Math.min (524288, (SelectedFile.size-Place))); FReader.readAsBinaryString (NewFile);); funksjon UpdateBar (prosent) document.getElementById ('ProgressBar'). style.width = prosent + '%'; document.getElementById ('prosent'). innerHTML = (Math.round (prosent * 100) / 100) + '%'; var MBDone = Math.round (((prosent / 100,0) * SelectedFile.size) / 1048576); document.getElementById ('MB'). innerHTML = MBDone; 

Med denne siste funksjonen er opplasteren fullført! Alt vi har igjen å gjøre er å flytte den ferdige filen ut av Temp / mappe og generere miniatyrbildet.


Trinn 7: Miniatyrbildet

Før vi genererer miniatyrbildet, må vi flytte filen ut av den midlertidige mappen. Vi kan gjøre dette ved å bruke filstrømmer og pumpe metode. De pumpe Metoden tar inn en lese- og skrive-strøm, og bufferer dataene over. Du bør legge til denne koden der jeg skrev 'Generer miniatyrbilde her' i Laste opp begivenhet:

 var inp = fs.createReadStream ("Temp /" + Navn); var ut = fs.createWriteStream ("Video /" + Navn); util.pump (inp, ut, funksjon () fs.unlink ("Temp /" + Navn, funksjon () // Dette sletter den midlertidige filen // Moving File Completed););

Vi har lagt til fjernlink-kommandoen; Dette vil slette den midlertidige filen, etter at vi har fullført kopieringen. Nå på miniatyrbildet: Vi bruker ffmpeg til å generere miniatyrbildene, fordi det kan håndtere flere formater, og det er en kino å installere. På dette tidspunktet er det ikke noen gode ffmpeg-moduler, så vi bruker exec kommando, som tillater oss å utføre Terminalkommandoer fra Node.js.

 exec ("ffmpeg -i Video /" + Navn + "-ssid 01:30 -r 1 -an -vframes 1 -f mjpeg Video /" + Navn + ".jpg", funksjon (err) socket.emit (' Ferdig ', ' Bilde ':' Video / '+ Navn +' .jpg '););

Denne ffmpeg-kommandoen genererer en miniatyrbilde på 1:30 -merket, og lagrer det til video / mappe med a .jpg filtype. Du kan redigere tiden på miniatyrbildet ved å endre -ss parameter. Når miniatyrbildet er generert, sender vi ut Ferdig begivenhet. La oss nå gå tilbake til HTML-siden og implementere den.


Trinn 8: Etterbehandling

De Ferdig hendelsen vil fjerne fremdriftslinjen og erstatte den med miniatyrbildet. Fordi Node.js ikke er konfigurert som en webserver, må du plassere serveren din (for eksempel Apache) i Sti variabel, for å laste bildet.

 var sti = "http: // localhost /"; socket.on ('Done', funksjon (data) var Content = "Video suksessfullt lastet opp !!" Innhold + = "
"; Innhold + ="