Comment créer un téléchargeur de vidéos pouvant être repris dans Node.js

Si vous avez déjà téléchargé un fichier vidéo considérablement volumineux, alors vous connaissez ce sentiment: vous avez terminé à 90%, et vous avez actualisé la page par inadvertance - vous devez tout recommencer à zéro.

Dans ce didacticiel, je vais vous montrer comment créer pour votre site un outil de téléchargement vidéo capable de reprendre un téléchargement interrompu et de générer une miniature une fois l'opération terminée..


Intro

Pour que cet outil de téléchargement puisse être repris, le serveur doit savoir combien de fichiers ont déjà été téléchargés et pouvoir continuer là où il s'était arrêté. Pour accomplir cette tâche, nous allons donner le contrôle total au serveur Node.js pour demander des blocs de données spécifiques, et le formulaire HTML récupérera ces demandes et enverra les informations nécessaires au serveur..

Pour gérer cette communication, nous utiliserons Socket.io. Si vous n'avez jamais entendu parler de Socket.io, il s'agit d'un cadre pour la communication en temps réel entre Node.js et une page Web HTML..

C'est le concept de base. nous allons commencer avec le formulaire HTML.


Étape 1: le HTML

Je vais garder le HTML assez simple; tout ce dont nous avons besoin est une entrée pour choisir un fichier, une zone de texte pour le nom et un bouton pour commencer le téléchargement. Voici le code nécessaire:

  

Téléchargeur de vidéo



Notez que j'ai enveloppé le contenu dans une durée; nous l'utiliserons plus tard pour mettre à jour la mise en page de la page avec JavaScript. Je ne vais pas couvrir le CSS dans ce tutoriel, mais vous pouvez télécharger le code source, si vous souhaitez utiliser le mien.


Étape 2: le faire fonctionner

HTML5 est encore relativement nouveau et n'est pas encore totalement pris en charge par tous les navigateurs. Avant de poursuivre, la première chose à faire est de s'assurer que le navigateur de l'utilisateur prend en charge les API de fichier HTML5 et la classe FileReader..

La classe FileReader nous permet d'ouvrir et de lire des parties d'un fichier et de transmettre les données sous forme de chaîne binaire au serveur. Voici le code JavaScript pour la détection des fonctionnalités:

 window.addEventListener ("load", Prêt); function Ready () if (window.File && window.FileReader) // Ce sont les objets HTML5 pertinents que nous allons utiliser document.getElementById ('UploadButton'). addEventListener ('click', StartUpload); document.getElementById ('FileBox'). addEventListener ('change', FileChosen);  else document.getElementById ('UploadArea'). innerHTML = "Votre navigateur ne prend pas en charge l'API de fichier. Veuillez mettre à jour votre navigateur"; 

Le code ci-dessus ajoute en outre des gestionnaires d'événements au bouton et au fichier saisis dans le formulaire. le FileChosen function définit simplement une variable globale avec le fichier - afin que nous puissions y accéder ultérieurement - et remplit le champ de nom, de sorte que l'utilisateur ait un point de référence lorsqu'il nomme le fichier. Voici la FileChosen une fonction:

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

Avant d'écrire le Commence le téléchargement fonction, nous devons configurer le serveur Node.js avec socket.io; prenons-en soin maintenant.


Étape 3: le serveur Socket.io

Comme je l'ai mentionné précédemment, j'utiliserai Socket.io pour la communication entre le serveur et le fichier HTML. Pour télécharger Socket.io, tapez npm installer socket.io dans une fenêtre de terminal (en supposant que vous avez installé Node.js), une fois que vous avez accédé à ce répertoire de projets. Le fonctionnement de socket.io est le suivant: soit le serveur, soit le client "émet" un événement, puis l’autre partie récupère cet événement sous la forme d’une fonction avec possibilité de transmettre des données JSON dans les deux sens. Pour commencer, créez un fichier JavaScript vide et placez-y le code suivant..

 var app = require ('http'). createServer (gestionnaire), io = require ('socket.io'). listen (app), fs = require ('fs'), exec = require ('child_process'). exec , util = require ('util') app.listen (8080); gestionnaire de fonctions (req, res) fs.readFile (__ dirname + '/index.html', function (err, données) if (err) res.writeHead (500); renvoie res.end ('Erreur de chargement de l'index. html '); res.writeHead (200); res.end (données););  io.sockets.on ('connection', function (socket) // Les événements iront ici);

Les cinq premières lignes incluent les bibliothèques requises, la ligne suivante indique au serveur d'écouter sur le port 8080 et la fonction de gestionnaire transmet simplement le contenu de notre fichier HTML à l'utilisateur lorsqu'il accède au site..

Les deux dernières lignes sont le gestionnaire socket.io et seront appelées lorsque quelqu'un se connectera, via Socket.io.

Maintenant, nous pouvons revenir au fichier HTML et définir des événements socket.io.


Étape 4: Quelques événements Socket.io

Pour commencer à utiliser Socket.io dans notre page, nous devons d’abord créer un lien vers sa bibliothèque JavaScript. Vous faites cela de la même manière que vous référencez n'importe quelle bibliothèque: faites-la référence dans la zone principale. Ajoutez ce qui suit à la page, avant vos scripts, évidemment.

Ne vous inquiétez pas pour obtenir ce fichier, car il est généré à l'exécution par le serveur Node.js..

Maintenant, nous pouvons écrire le Commence le téléchargement fonction que nous avons connecté à notre bouton:

 var socket = io.connect ('http: // localhost: 8080'); var FReader; Nom var; function StartUpload () if (document.getElementById ('FileBox'). value! = "") FReader = new FileReader (); Name = document.getElementById ('NameBox'). Value; var Content = "Chargement de "+ SelectedFile.name +" en tant que "+ Name +""; Contenu + = '
0%'; Contenu + = " - 0/ "+ Math.round (SelectedFile.size / 1048576) +" MB"; document.getElementById ('UploadArea'). innerHTML = Contenu; FReader.onload = fonction (evnt) socket.emit ('Upload', 'Nom': Nom, Données: evnt.target.result); socket.emit ('Démarrer', 'Nom': Nom, 'Taille': SelectedFile.size); else alert ("Veuillez sélectionner un fichier");

La première ligne se connecte au serveur Socket.io; Ensuite, nous avons créé deux variables pour le lecteur de fichiers et le nom du fichier, car nous aurons besoin d'un accès global à celles-ci. Dans la fonction, nous nous sommes d'abord assurés que l'utilisateur sélectionne un fichier et, le cas échéant, nous créons le fichier. FileReader, et mettre à jour le DOM avec une jolie barre de progression.

Le lecteur de fichier en charge La méthode est appelée chaque fois qu’elle lit des données; tout ce que nous devons faire est d’émettre un Télécharger événement et envoyez les données au serveur. Enfin, nous émettons un Début événement, en transmettant le nom et la taille du fichier au serveur Node.js.

Revenons maintenant au fichier Node.js et implémentons des gestionnaires pour ces deux événements..


Étape 5: Gestion des événements

Vous devez vider la mémoire tampon de temps en temps, sinon le serveur tombera en panne, à cause d'une surcharge de mémoire..

Les événements socket.io vont dans le gestionnaire que nous avons sur la dernière ligne de notre fichier Node.js. Le premier événement que nous allons implémenter est le Début événement, qui est déclenché lorsque l'utilisateur clique sur le Télécharger bouton.

J'ai mentionné plus tôt que le serveur devrait contrôler quelles données il veut recevoir ensuite; cela lui permettra de continuer à partir d'un téléchargement précédent qui était incomplet. Pour ce faire, il commence par déterminer s’il existe un fichier portant ce nom qui n’a pas terminé son téléchargement et, dans l’affirmative, il continuera à partir de là où il s’est arrêté; sinon, cela commencera au début. Nous allons transmettre ces données par incréments d'un demi-mégaoctet, ce qui donne 524288 octets..

Afin de garder une trace des différents téléchargements se produisant en même temps, nous devons ajouter une variable pour tout stocker. En haut de votre fichier, ajoutez var Fichiers = ; ' Voici le code pour le Début un événement:

 socket.on ('Start', function (data) // data contient les variables que nous avons transmises dans le fichier html var Name = data ['Name']; Fichiers [Name] = // Créer une nouvelle entrée dans La variable de fichier Taille du fichier: data ['Size'], data: "", Téléchargé: 0 var Place = 0; try var Stat = fs.statSync ('Temp /' + Nom); if (Stat.isFile () ) Fichiers [Nom] ['Téléchargé'] = Stat.size; Place = Stat.size / 524288; catch (er)  // C'est un nouveau fichier fs.open ("Temp /" + Nom, " a ", 0755, function (err, fd) if (err) console.log (err); else Fichiers [Nom] ['Gestionnaire'] = fd; // Nous stockons le gestionnaire de fichiers pour pouvoir écrire à elle plus tard socket.emit ('MoreData', 'Place': Place, Percent: 0)););

Tout d'abord, nous ajoutons le nouveau fichier au Des dossiers tableau, avec la taille, les données et la quantité d’octets téléchargés jusqu’à présent. le Endroit magasins de variables où nous sommes dans le fichier - la valeur par défaut est 0, ce qui est le début. Nous vérifions ensuite si le fichier existe déjà (c’est-à-dire qu’il était au milieu et s’est arrêté) et mettons à jour les variables en conséquence. Qu'il s'agisse d'un nouveau téléchargement ou non, nous ouvrons maintenant le fichier pour l'écrire sur le Temp / dossier, et émettre le MoreData événement pour demander la prochaine section de données du fichier HTML.

Maintenant, nous devons ajouter le Télécharger événement, qui, si vous vous en souvenez, est appelé chaque fois qu'un nouveau bloc de données est lu. Voici la fonction:

 socket.on ('Upload', fonction (data) var Nom = data ['Nom']; Fichiers [Nom] ['Téléchargé'] + = data ['Données']. length. Fichiers [Nom] ['Données '] + = data [' Data ']; if (Fichiers [Nom] [' Téléchargé '] == Fichiers [Nom] [' Taille du fichier ']]) // Si le fichier est entièrement téléchargé fs.write (Fichiers [Nom] ['Gestionnaire'], Fichiers [Nom] ['Données'], null, 'Binaire', fonction (err, Écrite) // Obtenir la vignette ici); autre si (Fichiers [Nom] ['Données']] .length> 10485760) // Si le tampon de données atteint 10 Mo fs.write (Fichiers [Nom] ['Gestionnaire'], Fichiers [Nom] ['Données']], null, 'Binaire', fonction (err, Writen) Fichiers [Nom] ['Données'] = "" // Réinitialiser le tampon var Place = Fichiers [Nom] ['Téléchargé'] / 524288; var Pourcent = (Fichiers [Nom] ['Téléchargé'] / Fichiers [ Name] ['FileSize']) * 100; socket.emit ('MoreData', 'Place': Place, 'Pourcent': Pourcent);); var Place = Fichiers [Nom] ['Téléchargé '] / 524288; var Pourcent = (Fichiers [Nom] [' Téléchargé '] / Fichiers [Nom] [' TailleTichier ']]) * 100; socket.emit (' MoreData ', ' Place ': Place,' Pourcent ' : Pourcentage););

Les deux premières lignes de ce code mettent à jour le tampon avec les nouvelles données et mettent à jour le nombre total d'octets téléchargés. Nous devons stocker les données dans une mémoire tampon et les enregistrer par incréments, de manière à ce que le serveur ne tombe pas en panne en raison d'une surcharge de mémoire. tous les dix mégaoctets, nous allons enregistrer et effacer le tampon.

La première si instruction détermine si le fichier est complètement téléchargé, la seconde vérifie si la mémoire tampon a atteint 10 Mo, et enfin, nous demandons MoreData, en passant le pour cent fait et le prochain bloc de données à récupérer.

Maintenant, nous pouvons revenir au fichier HTML et implémenter le MoreData événement et mettre à jour les progrès.


Étape 6: Suivre les progrès

J'ai créé une fonction pour mettre à jour la barre de progression et la quantité de Mo téléchargée sur la page. En plus de cela, le Plus de données event lit le bloc de données demandé par le serveur et le transmet au serveur.

Pour diviser le fichier en blocs, nous utilisons l’API de fichier. Tranche commander. Comme l’API de fichier est encore en développement, nous devons utiliser webkitSlice et MozSlice pour les navigateurs Webkit et Mozilla, respectivement.

 socket.on ('MoreData', fonction (data) UpdateBar (data ['Pourcentage']); var Place = data ['Place'] * 524288; // La position de départ des blocs suivants var NewFile; // La variable qui tiendra le nouveau bloc de données si (SelectedFile.webkitSlice) NewFile = SelectedFile.webkitSlice (Lieu, Lieu + Math.min (524288, (SelectedFile.size-Place))); sinon NewFile = SelectedFile.mozSlice (Lieu, Lieu + Math.min (524288, (SelectedFile.size-Place))); FReader.readAsBinaryString (NewFile);); function UpdateBar (pourcent) document.getElementById ('ProgressBar'). style.width = pourcent + '%'; document.getElementById ('pourcent'). innerHTML = (Math.round (percent * 100) / 100) + '%'; var MBDone = Math.round (((percent / 100.0) * SelectedFile.size) / 1048576); document.getElementById ('MB'). innerHTML = MBDone; 

Avec cette fonction finale, le téléchargeur est terminé! Tout ce qui nous reste à faire est de déplacer le fichier terminé du Temp / dossier et générer la vignette.


Étape 7: la vignette

Avant de générer la vignette, nous devons déplacer le fichier hors du dossier temporaire. Nous pouvons le faire en utilisant des flux de fichiers et le pompe méthode. le pompe La méthode prend un flux de lecture et d’écriture et met les données en mémoire tampon. Vous devriez ajouter ce code où j'ai écrit 'Generate Thumbnail here' dans le Télécharger un événement:

 var inp = fs.createReadStream ("Temp /" + Nom); var out = fs.createWriteStream ("Video /" + Nom); util.pump (inp, out, function () fs.unlink ("Temp /" + Nom, function () // Ceci supprime le fichier temporaire // déplacement du fichier terminé););

Nous avons ajouté la commande unlink. cela supprimera le fichier temporaire une fois que nous aurons fini de le copier. Passons maintenant à la vignette: nous allons utiliser ffmpeg pour générer les vignettes, car il peut gérer plusieurs formats et constitue un cinch à installer. Au moment d'écrire ces lignes, il n'y a pas de bons modules ffmpeg, nous allons donc utiliser le exec commande, qui nous permet d'exécuter des commandes Terminal depuis Node.js.

 exec ("ffmpeg -i Video /" + Nom + "-ss 01:30 -r 1 -an -v cadres 1 -f mjpeg Vidéo /" + Nom + ".jpg", function (err) socket.emit (' Terminé ', ' Image ':' Vidéo / '+ Nom +' .jpg '););

Cette commande ffmpeg générera une vignette à la marque 1:30 et la sauvegardera dans la Vidéo/ dossier avec un .jpg Type de fichier. Vous pouvez modifier l’heure de la vignette en modifiant le -ss paramètre. Une fois la vignette générée, nous émettons le message. Terminé un événement. Revenons maintenant à la page HTML et implémentons-la..


Étape 8: Terminer

le Terminé event supprimera la barre de progression et la remplacera par la vignette. Node.js n'étant pas configuré en tant que serveur Web, vous devez placer l'emplacement de votre serveur (Apache, par exemple) dans le répertoire. Chemin variable, afin de charger l'image.

 var Path = "http: // localhost /"; socket.on ('Terminé', fonction (données) var Content = "Vidéo téléchargée avec succès !!" Content + = "
"; Contenu + ="