{firstname:'Stéphane' , lastname : 'Michel', job: 'Software Craftsman'}
© Stéphane Michel
GET http://server:8080/index.html
client
serveur
POST /DateCreation HTTP/1.0 Host: www.xyz.org Content-Type: text/xml; charset = utf-8 Content-Length: nnn <?xml version = "1.0"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope" SOAP-ENV:encodingStyle = "http://www.w3.org/2001/12/soap-encoding"> <SOAP-ENV:Body xmlns:m = "http://www.xyz.org/datecreation"> <m:GetDateCreation> <m:SchoolName>ESIR</m:SchoolName> </m:GetDateCreation> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
HTTP/1.0 200 OK Content-Type: text/xml; charset = utf-8 Content-Length: nnn <?xml version = "1.0"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope" SOAP-ENV:encodingStyle = "http://www.w3.org/2001/12/soap-encoding"> <SOAP-ENV:Body xmlns:m = "http://www.xyz.org/datecreation"> <m:GetDateCreationResponse> <m:DateCreation>mars 2010</m:DateCreation> </m:GetDateCreationResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
client
serveur
GET /DateCreation/ESIR HTTP/1.0 Host: www.xyz.org Content-Type: text/xml; charset = utf-8 Content-Length: nnn
HTTP/1.0 200 OK Content-Type: application/json; charset = utf-8 Content-Length: nnn {"creationDate":"mars 2010"}
Des méthodes HTTP
Des codes de retour HTTP
De bonnes pratiques
GET http://serv1/
GET http://serv1/
GET http://serv2/
GET http://serv2/
GET http://serv3/
GET http://serv3/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
ROUTER / PROXY / GATEWAY
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
ROUTER / PROXY / GATEWAY
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
ROUTER / PROXY / GATEWAY
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
ROUTER / PROXY / GATEWAY
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
GET http://serv/
ROUTER / PROXY / GATEWAY
JWT
JWT
JWT
JWT
JWT
JWT
JWT
JWT
JWT
JWT
JWT
JWT
Créé par Ryan Dahl en 2009
Voir ici pour plus de détails
En quelques mots
Moteur JavaScript Chrome V8
Modèle d'E/S non bloquant
Enorme ecosystème de modules (npm ou yarn)
Près de 350 000 modules
Plus de 35 000 màj par semaine
102 000 publishers
Version paires : versions maintenues
Maintenance : LTS (Long Term Support)
Source Stackify
Source GitHut
// HelloWorld.js const os = require('os') console.log('Hello World!') console.log(`Running on ${os.hostname} computer and ${os.platform} operating system.`)
[16:07] sogitec@MACPRO8:firstApp $ node --version v8.4.0 [16:08] sogitec@MACPRO8:firstApp $ node HelloWorld.js Hello World! Running on MACPRO8.local computer and darwin operating system.
HelloWorld.js
Exécution du programme
Home Page
Si vous n'y trouvez pas votre bonheur, sous Linux : binaires pour de multiples distributions
Téléchargements
Si vous souhaitez disposer de plusieurs version de node/npm, la solution est
Node Version Manager
Temps d'accès aux données
RAM : nanoseconds
DD / réseau : milliseconds
Vitesse de transfert
RAM : GB/s
Réseau : MB/s -> GB/s
Qu'elles soient réseau ou disque les E/S sont dramatiquement lentes.
De plus elles sont généralement bloquantes.
Pas facile de faire des accès concurrents dans ses conditions.
Une solutions classiques des serveurs web est d'utiliser du multi thread ou du multi process.
Source : node.js Design Patterns
C'est facile à faire (fork) mais...
La mémoire complète (code, données, tas et pile) du process est dupliquée...
Communication inter-process lentes : on passe par l'OS
C'est ce qui a donné l'idée du multi-thread
Les threads partagent la même mémoires et les mêmes ressources.
La mise au point d'un programme multi-thread est complexe.
Communication inter-thread est rapide.
Mais le CPU ne dispose pas d'autant de coeurs qu'il y a de threads...
L'orchestration des threads sur les différents coeurs du processeurs est complexe et coûteuse en ressources (commutation de contexte).
Voir ici pour plus de détails
...par nature "mono-thread"
Source : node.js Design Patterns
1 thread principal pour le code JavaScript
1 pool de threads (workers-pool basé sur libuv) pour les traitements coûteux en I/O (file, DNS, etc.) ou en CPU (crypto, zip, etc.),
Voir ici pour plus de détails
Le tout orchestré par une architecture orientée événement très efficace (Event-driven Architecture)
En conséquence, pour un maximum de fluidité (on dit "pour que ça scale bien"), les traitements dans un callback doivent être les plus cours possibles.
Avec node.js tous les clients (cas d'un serveur web) partagent le même thread.
La prise en compte des requêtes clientes en attente ne se fait que si le callback en cours est terminé.
Source : node.js Design Patterns
V8 : moteur Javascript rapide et efficace au niveau mémoire
Des wrappers pour exposer la lib libuv de gestion des E/S non bloquantes
Une API Javascript (node-code) de haut niveau
Voir détail ici
Blocking methods execute synchronously and non-blocking methods execute asynchronously.
Exemples d'appels potentiellement bloquants :
Appel synchrone bloquant
Appel asynchrone non-bloquant
Voir détail ici
Les appels asynchrones sont à privilégier puisqu'ils sont non bloquants. Ils rendent la main à l'EventLoop qui se charge de gérer les exécutions concurrentes.
Ce mécanisme est très différent de ce que l'on trouve dans d'autres langages où c'est plutôt l'ajout de nouveaux thread qui permet de gérer les traitements concurrents.
Remarque : la gestion des thread est très gourmande en ressource (mémoire, cpu (changement de contexte)).
Voir détail ici
Principal (et premier) gestionnaire de modules de node.js
Alternative possible : yarn
A faillit remplacer npm en proposant des performances bien supérieures en 2016, mais npm a rattrapé son retard depuis
yarn vs npm ici
p : popularité
q : qualité
m : maintenance
Critères de choix
Nb téléchargement, évolution dans le temps
Test unitaire,
couverture de code
Anomalies,
délais de correction
Si vous hésitez entre plusieurs modules similaires
Etat des lieux 2017 (statistiques d'utilisatrion)
(Exemple pour le back-end)
Etat des lieux 2018
npm init
npm install --save <module>
npm i --save <module>
Documentation officielle ici
npm i -g <module>
npm install -g <module>
A éviter dans la mesure du possible car ses modules ne sont pas associés aux projets.
Intéressant spour les modules très lourd (gain de place)
npm uninstall --save <module>
npm uninstall --save-dev <module>
npm uninstall -g <module>
npm ci
npm i
npm i npm@latest -g
{
"name": "firstproject",
"version": "0.0.1",
"description": "My first project",
"main": "server.js",
"scripts": {
"start": "nodemon server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "sm",
"license": "ISC",
"dependencies": {
"express": "^4.16.3",
"winston": "^3.1.0"
},
"devDependencies": {
"mocka": "0.0.1"
}
}
npm i --save express npm i --save winston
npm i --save-dev mocka
Plus de détails ici
console
console.log('hello %s', 'world')
console.error(new Error('Whoops, something bad happened')) console.assert(false, 'Whoops %s work', 'didn\'t')
console.count('abc')
abc: 1
console.time('100-elements') for (let i = 0; i < 100; i++) {} console.timeEnd('100-elements') // prints 100-elements: 225.438ms
console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }]) // ┌─────────┬─────┬─────┐ // │ (index) │ a │ b │ // ├─────────┼─────┼─────┤ // │ 0 │ 1 │ 'Y' │ // │ 1 │ 'Z' │ 2 │ // └─────────┴─────┴─────┘
util
const util = require('util')
util.format('hello %s', 'world') hello world util.log('sortie avec timestamp') 28 Oct 17:55:52 - sortie avec timestamp console.log( util.inspect({ value: 'foo', items: [ 'a', 'b' ], fonc: hello }) ) { value: 'foo', items: [ 'a', 'b' ], fonc: [Function: hello] }
process
// Accès aux variables d'environnement console.log(process.env.PATH)
// Version de Node et libs sous-jacentes
console.log(process.version, process.versions)
// OS d'exécution console.log(process.platform, process.arch) // Paramètres de l'appli console.log(process.argv)
// Events sur le process process.on('exit', (code) => { console.log(`About to exit with code: ${code}`) }) process.on('uncaughtException', (err) => console.error('Kesessa ?', err))
os
const os = require('os') // Caractère de fin de ligne sur la plateforme courante console.log(os.EOL) // Info sur l'OS, idem process.arch et process.platform console.log(os.platform(), os.release(), os.arch(), os.type())
// Détails processeurs et interfaces réseau,
// similaires à cat /proc/cpuinfo et ifconfig
console.log(os.cpus(), os.networkInterfaces()) // Répertoire home de l'utilisateur courant console.log(os.homedir()) // Utilisation mémoire (niveau OS, ne
// pas confondre avec process.memoryUsage() console.log(os.totalmem(), os.freemem())
fs
const fs = require('fs') // Lecture d'un fichier (par défaut, méthode asynchrone) fs.open('/open/some/file.txt', 'r', (err, fd) => {
if (err) throw err fs.close(fd, (err) => { if (err) throw err }) }) // Observer les modifications dans un répertoire fs.watch('./tmp', { encoding: 'buffer' }, (eventType, filename) => { if (filename) { console.log(filename) } }) // lecture de répertoire et informations sur un fichier fs.readdir('.', (err, files) => { // ici il faudrait tester err files.forEach((file) => { fs.stat(path.join(__dirname, file), (err, stat) => { // idem console.log('%s : %d octets, dernière modif. %j',file,stat.size,stat.mtime) }) }) })
crypto
Ensemble de wrapper autour de OpenSSL
Certificate : Génération et manipulation de certificats
Sign, Verify : signature et vérification de signature
Hash : génération de hashcode
Cipher, Decipher : cryptage, décryptage
Crypto : Point d'entrée du module, génération de clefs asymétriques, etc.
et de nombreux autres
Assert : Test d'assertions
Child Process : gestion des multi-process
Cluster : multi-thread
Errors : Gestion des erreurs (try, catch, finally)
Events : Gestion des événements (détaillé plus loin)
http, https : gestion de la pile Http(s)
Stream : Gestion des flux (détaillé plus loin)
TTY : Interaction avec la console
UDP/Datagram : réseau bas niveau, sockets
URL : Manipulation d'URL
Zlib : compactage / décompactage de fichiers
Outillage sur les tableaux, map, etc.
each, map, reduce, filter, etc.
Intéressant si on ne dispose pas de ES6 (voir ici pour plus de détail).
Outil de manipulation de dates.
A peut prêt toutes les opérations que l'on peut imaginer faire sur des dates : parsing, validation, comparaison, formattage.
Outil de manipulation de dates.
Plus de 140 opérations sur les dates.
Peut être un peu plus facile d'utilisation que moment, mais c'est question de goût...
Il est à moment.js ce que lo-dash est à underscore... une alternative qui a dépassée sur certains points l'original...
Gestion des log dans l'application (console, fichier, etc.)
const logger = winston.createLogger({ transports: [ new winston.transports.Console(), new winston.transports.File({ filename: 'combined.log' }) ] }) ...
logger.log({level:'info',message:'Premier log de type info'})
// Raccourcis
logger.log('Log de type info (par défaut)')
logger.error('Une erreur')
logger.warn('Un warning')
logger.debug('Une trace de debug')
Similaire à Winston mais propose par défaut un log au format JSON pour lequel il propose un client (CLI) pour leur visualisation.
const bunyan = require('bunyan') const log = bunyan.createLogger({ name: 'myapp',
streams: [ { stream: process.stdout }, { path: 'logs/dev.log' } ]
}) log.info('hi') log.warn({lang: 'fr'}, 'au revoir')
Contrôle de type sur les objets (sans aller jusqu'à faire du TypeScript).
Permet d'imposer une structure à un objet ou un type à un paramètre de fonction.
Remonte une exception si la structure n'est pas respectée.
Permet de réduire les effets de bord en particulier lorsque l'on a pas la main sur la source des données.
import t from 'tcomb' const GPS = t.struct({ lat: t.Number, lon: t.Number, }, {strict: true}) const Bordeaux = GPS({ lat: 44.8638, lon: -0.6561 }) // OK const Crest = GPS({ lat: 44.7311, long: 4.9861 }) // erreur : long const Toulouse = GPS({ lat: 43.6008, lon: 'r0s3' } // erreur : 'r0s3'
import t from 'tcomb'
function sum(a, b) {
t.Number(a)
t.Number(b)
return a + b
}
sum(1, 's') // throws '[tcomb] Invalid value "s"
// supplied to Number'
Outil de vérification syntaxique pour ECMAScript.
Bonnes pratiques, etc.
npm install --save-dev eslint
// Création du fichier de config (à faire une fois)
./node_modules/.bin/eslint --init
// Lancer eslint sur un fichier js
./node_modules/.bin/eslint myFile.js
S'utilise en ligne de commande ou plus simplement directement intégré à l'IDE (VSCode par exemple via le plugin ESLint de Dirk Baeumer).
Permet de réaliser un serveur HTTP
Décrit en détail dans le module Express du cours.
var express = require('express') var app = express() app.get('/', function (req, res) { res.send('Hello World') }) app.listen(3000)
Aglomère un ensemble de modules qui œuvrent pour la sécurisation des serveurs HTTP
const express = require('express') const helmet = require('helmet') const app = express() app.use(helmet())
...
ModuleDefault?
contentSecurityPolicy for setting Content Security Policy | |
crossdomain for handling Adobe products' crossdomain requests | |
dnsPrefetchControl controls browser DNS prefetching | ✓ |
expectCt for handling Certificate Transparency | |
featurePolicy to limit your site's features | |
frameguard to prevent clickjacking | ✓ |
hidePoweredBy to remove the X-Powered-By header | ✓ |
hpkp for HTTP Public Key Pinning | |
hsts for HTTP Strict Transport Security | ✓ |
ieNoOpen sets X-Download-Options for IE8+ | ✓ |
noCache to disable client-side caching | |
noSniff to keep clients from sniffing the MIME type | ✓ |
referrerPolicy to hide the Referer header | |
xssFilter adds some small XSS protections | ✓ |
Module : boîte dans laquelle on pourra embarquer une collection de méthodes ou de variables liées entre elles et qui formeront un ensemble cohérent (Framework).
Le module détermine l’accessibilité de chaque membre.
Pour le code qui la consomme, il s’agit donc d’une boîte noire.
Syntaxe historique (avant ES6) popularisée par node.js pour créer des modules
plus de détails ici http://wiki.commonjs.org/wiki/Modules/1.1
// HelloWorld.js const os = require('os') exports.hello = function(){
console.log('Hello World!') console.log( `Running on ${os.hostname} computer and ${os.platform} operating system.` )
}
// index.js const hello = require('./HelloWorld') hello.hello()
[16:40] sogitec@MACPRO8:firstApp $ node index.js Hello World! Running on MACPRO8.local computer and darwin operating system.
// lib.js
const PI = 3.1415926;
function sum(...args) {
log('sum', args)
return args.reduce((num, tot) => tot + num);
}
function mult(...args) {
log('mult', args);
return args.reduce((num, tot) => tot * num);
}
// private function
function log(...msg) {
console.log(...msg)
}
export { PI, sum, mult }
export default sum
Plus de détails ici
// Import complet d'un module
import * as lib from './lib.js'
console.log( lib.mult(1,2,3,4) ) // 24
// Import par défaut
import maFonction from './lib.js'
console.log( maFonction(1,2,3,4) ) // 10 (maFonction = sum)
// Renommage
import { PI as monPi} from './lib.js'
// Import d'une sous partie d'un module
import { sum } from './lib.js'
console.log( sum(1,2,3,4) ) // 10
Plus de détails ici
( require ou import)
PAS d'extension dans le require ou l'import
... = require('monModule') import ... as 'monModule'
ou
( require ou import)
l'algorithme précis est décrit ici https://nodejs.org/api/modules.html#modules_all_together
Plus de détails ici
Les modules sont mis en cache au premier chargement.
Si un module est importé plusieurs fois dans un projet, c'est le même objet qui est partagé !
Cas particulier des système non case sensitive (windows) :
require('monmodule') require('MonModule')
correspondent au même fichier mais retourneront deux objets différents
Bonne pratique : Toujours mettre le nom des modules en lowercase
// lib.js let compteur = 0 const increase = () => { compteur++ } const current = () => { return compteur } export default { current, increase }
//index.js import lib1 from './lib.js' import lib2 from './lib.js' console.log(`${lib1.current()}, ${lib2.current()}`) lib1.increase() console.log(`${lib1.current()}, ${lib2.current()}`) lib2.increase() console.log(`${lib1.current()}, ${lib2.current()}`)
Sortie attendue :
0, 0 1, 1 2, 2
git init
Initialisation du repository GIT
Initialisation nouveau projet
npm init
Création d'un fichier .gitignore avec https://www.gitignore.io/
Commiter .gitignore lors du premier commit
git add . git commit -m "Initialisation du projet"
Les sources sous /src
Les sources compilées par babel seront sous /build
npm install --save-dev @babel/core @babel/cli @babel/preset-env npm install --save @babel/polyfill
Installation modules babel
Configuration babel pour node.js
// babel.config.js const presets = [ ['@babel/env', { targets: { node: 'current'}, useBuiltIns: 'usage'} ]] module.exports = { presets }
Configuration package.json pour compiler et lancer le projet
{"name": "monappli", "version": "1.0.0", "description": "ma description", "main": "src/index.js", "scripts": { "build": "npx babel src --out-dir build", "start": "npm run build && node build/index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "devDependencies": { "@babel/cli": "^7.1.2", "@babel/core": "^7.1.2", "@babel/preset-env": "^7.1.0" }, "dependencies": { "@babel/polyfill": "^7.0.0" }}
Configuration package.json pour compiler et lancer le projet
npm start
npm run build && node build/index.js
npx babel src --out-dir build
Execution de build/index.js
"Compilation" des fichiers de src/ dans le répertoire build/
npm install -g nodemon
Relance automatiquement l'application lorsqu'une modification de source est réalisée
En phase de développement
// S'utilise à la place de node pour lancer une application nodemon server.js // au lieu de "node server.js"
npm install npm-run-all --save-dev
Permet de lancer des scripts en parallèle ou en séquence en gérant les aspects cross plateforme (le & pour lancer des process en parallèle ne fonctionne pas sous windows).
{ "...": "...",
"scripts": { "build": "npm-run-all -p 'build:*' -s start",
"build:css": "sass ...",
"build:js": "browserify ...",
"start": "node server.js" }
}
// puis
npm run scripts
S'utilise en ligne de commande ou plus généralement avec npm en configurant package.json
npm install husky --save-dev
S'intègre avec GIT et permet de lancer des traitements/contrôles avant de réaliser des "git push", des "git commit" ou des "git pull" (liste complète des hooks ici)
{ "...": "...",
"scripts": {
"precommit": "lint-staged",
"post-merge": "npm install" } }
// puis
git commit -m "...
S'utilise en configurant package.json
npm install debug --save-dev
Permet de disposer de traces limitées à une sous-partie applicative
import { debug } from "debug" const debugA = debug("module:a") const debugB = debug("module:b") const debugHTTP = debug("http") debugA("Trace from moduleA") debugB("Trace from moduleA") debugHTTP("Trace from HTTP ...")
$ DEBUG=module:b node build/index.js module:b Trace from moduleA +0ms
$ DEBUG=module:* node build/index.js module:a Trace from moduleA +0ms module:b Trace from moduleA +0ms
$ DEBUG=module:*,http node build/index.js module:a Trace from moduleA +0ms module:b Trace from moduleA +0ms http Trace from HTTP ... +0ms
npm install -g depcheck
Permet d'analyser les modules du projet pour détecter ceux qui ne servent à rien et ceux qui ne sont pas déclarés dans package.json
Communication via des fonctions d'écoute et d'émission de messages
Les événements 'error' sont spéciaux : faute de traitement, ils agiront comme une exception (stack trace sur stderr puis arrêt du processus).
Dans Node, la majorité des objets noyau émettent des événements : flux, requête/réponse, watchers, erreurs…
L’API est toujours la même, centralisée dans une classe EventEmitter
//index.js import EventEmitter from 'events' const emitter = new EventEmitter() emitter.on('alloffame', (date, name, dcd) => { console.log(`${name} born in ${date.getFullYear()} ${dcd ? ', DEAD' : ''}`) }) emitter.once('alloffame', (date, name, bissextil) => { console.log(`First All of Fame received for ${name}`) }) emitter.emit('alloffame', new Date('1930-05-11'), 'Edsger Dijkstra', true) emitter.emit('alloffame', new Date('1955-06-08'), 'Tim Berners-Lee')
Edsger Dijkstra born in 1930 , DEAD First All of Fame received for Edsger Dijkstra Tim Berners-Lee born in 1955
Voir ici pour des compléments
Permet de manipuler des volumes de données énormes sans utiliser beaucoup de ressources.
Le coeur I/O de node !
Buffering : on attend que le buffer soit plein avant d'envoyer le résultat au destinataire
Source : node.js design patterns
Source : node.js design patterns
const fs = require("fs") const zlib = require("zlib") const file = process.argv[2] fs.readFile(file, (err, buffer) => { zlib.gzip(buffer, (err, buffer) => { fs.writeFile(file + '.gz', buffer, err => { console.log('File successfully compressed') }) }) }
Exemple buffering : compression d'un fichier
Streaming : le flux est envoyé au destinataire au fur et à mesure qu'il est lu.
Source : node.js design patterns
Le streaming permet de réaliser des tâches qu'il n'est pas possible de faire avec le buffering comme lire un fichier de plusieurs Go car nécessiterait de mettre le contenu complet en mémoire...
De plus, le moteur Javascript V8 limite la taille des buffers à 0x3FFFFFFF octets soit un peu moins de 1Go.
Si le fichier est plus gros on part en 'out of memory' !
Moins gourmand en mémoire
Le destinataire du flux peut commencer à travailler dessus alors que l’émetteur du flux n'a pas encore terminé son émission.
Plus rapide
Source : node.js design patterns
const fs = require('fs') const zlib = require('zlib') const file = process.argv[2] fs.createReadStream(file) .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(file + '.gz') .on('finish', () => console.log('File successfully compressed') )
Exemple streaming : compression d'un fichier (à comparer avec la version buffering, 2 planches plus haut)
Un stream en node.js implémente l'une des 4 interfaces suivantes :
stream.Readable (2 modes : flowing et non-flowing)
stream.Writable
stream.Duplex
stream.Transform
Un stream est aussi une instance de EventEmitter pour la production d'événements :
const {createReadStream} = require('fs')
createReadStream(__filename, {highWaterMark: 100}) (1)
.on('end', () => console.log('Lecture terminée')) (2)
.on('data', (data) => { (3)
console.log('%d octets reçus', data.length)
})
.on('error', (err) => console.error(err)) (4)
Lecture de flux : flowing mode (ancienne méthode)
Voir doc createReadStream pour plus de détails
(1) taille du buffer : 100 octets (par défaut, 64ko)
(2) Appelé à la fin de la lecture
(3) Appelé pour chaque bloc (de 100 octets ici)
(3) Appelé si une erreur survient
Voir ici pour l'exemple d'origine
const {createReadStream} = require('fs')
const stream = createReadStream(__filename, {highWaterMark: 100}) (1)
.on('end', () => console.log('Lecture terminée')) (2)
.on('readable', () => { (3)
while (null !== (data=stream.read())) {
console.log('%d octets reçus', data.length)
})
})
.on('error', (err) => console.error(err)) (4)
Lecture de flux : non-flowing mode (nouvelle méthode)
(1) taille du buffer : 100 octets (par défaut, 64ko)
(2) Appelé à la fin de la lecture
(3) Appelé dès que le bloc est disponible
(3) Appelé si une erreur survient
Un peu plus de détails ici
const {createWriteStream, readFile} = require('fs')
const {join} = require('path')
const dest = join(__dirname, 'debug.txt')
const stream = createWriteStream(dest) (1)
stream.on('finish', () => {
readFile(dest, (error, data) => { (4)
console.log(String(data))
})
})
stream.on('error', (err) => console.error(err)) (5)
stream.write('Hell') (2)
stream.write('o Worl')
stream.end('d!') (3)
Ecriture de flux
Voir doc createReadStream pour plus de détails
(1) Création d'un flux vers le fichier debug.txt
(2) Ecriture dans le flux (encodage par défaut : utf8)
(3) end indique qu'il que se sont les dernières données
(4) l'event 'finish' est appelé suite au "end"
(5) Si une erreur survient
Voir ici pour l'exemple d'origine
flux bidirectionnel
Hérite à la fois de stream.Readable et de stream.Writable
Utile pour les objets qui peuvent être à la fois émettrice et réceptrice.
Exemple : socket réseau
Plus de détails ici
flux de transformation
Cas particulier de stream.Duplex
Comme lui, hérite à la fois de stream.Readable et de stream.Writable
Permet de réaliser des filtres de transformation sur des flux
Exemples : zlib ou crypto
Plus de détails ici
const {createReadStream, createWriteStream} = require('fs')
const {join} = require('path')
const filename_copy = join(__dirname, 'copie.js')
const source = createReadStream(__filename) (1)
const dest = createWriteStream(filename_copy) (2)
source.pipe(dest) (3)
.on('finish', () => console.log('Copie terminée !')) (4)
source.pipe(process.stdout) (5)
Les données lues depuis une source (Readable) sont redirigées vers une destination (Writeable) à l’aide de la fonction pipe()
(1) Création d'un flux de lecture
(2) Création d'un flux d'écriture
(3) Redirection du flux de lecture vers le flux d'écriture
(4) La redirection retourne le flux d’écriture, que l’on écoute pour savoir quand il a terminé d’écrire sur le disque
(5) Le flux de la source peut être redirigé vers plusieurs destinations
Voir ici pour l'exemple d'origine
Les flux utilisent par défaut des objets buffers.
Leur taille est définie à la création et est non modifiable.
Ils se comportent un peu comme un tableau d'integer.
Itérable avec for..of
'ascii' - For 7-bit ASCII data only. This encoding is fast and will strip the high bit if set.
'utf8' - Multibyte encoded Unicode characters. Many web pages and other document formats use UTF-8.
'utf16le' - 2 or 4 bytes, little-endian encoded Unicode characters. Surrogate pairs (U+10000 to U+10FFFF) are supported.
'ucs2' - Alias of 'utf16le'.
'base64' - Base64 encoding. When creating a Buffer from a string, this encoding will also correctly accept "URL and Filename Safe Alphabet" as specified in RFC4648, Section 5.
'latin1' - A way of encoding the Buffer into a one-byte encoded string (as defined by the IANA in RFC1345, page 63, to be the Latin-1 supplement block and C0/C1 control codes).
'binary' - Alias for 'latin1'.
'hex' - Encode each byte as two hexadecimal characters.
Encodage possibles (c.f. doc officielle)
Voir ici pour plus de détails
Ce qui fait la force de node.js c'est son moteur de pool threads
EventLoop permet à node.js d'exécuter des opérations I/O de manière non bloquante bien qu'il soir mono-thread !
Dans node.js les opérations I/O, et javascript se partagent un thread unique.
Pour de bonnes performances, node.js implique une programmation asynchrone!
Un projet node.js est défini par la présence d'un fichier package.json
node.js
Sites
node.js
Livres en ligne gratuits
Livres (bible)
npm
Vidéo
Sites