|
JavaScript HTML: Copia ficheros asíncrona JavaScript, cómo compilar JavaScript a ejecutable
Tutorial donde mostramos cómo crear un JavaScript que permite copiar una o varias carpetas de ficheros origen en uno o varios destinos de forma asíncrona, ejecutando varias copias concurrentemente. Explicamos también cómo usar Node.js para interpretar el JavaScript y cómo usar JXcore para generar un ejecutable .exe a partir del fichero JavaScript, permitiendo de esta forma ejecutar el JavaScript en cualquier PC sin necesidad de instalar software de terceros.
Funcionamiento y utilidad del código JavaScriptEl código JavaScript que mostraremos a continuación usa un fichero con formato JSON donde se introducen las carpetas origen a copiar y los distintos destinos de copia. Un ejemplo del contenido de este fichero JSON que llamaremos "actualizar.json": { "FACTURACION": { "origen": "\\\\servidor\\software\\Facturacion", "destinos": { "Domingo Días Festivos": "\\\\PC44\\c$\\Users\\Domingo\\Facturacion", "Juan Ramón Fuentes Limpias": "\\\\PC40\\c$\\Users\\Juan\\Facturacion", "Servidor Facturación": "\\\\SRVFACTURACION\\Facturacion" } }, "CONTABILIDAD": { "origen": "\\\\servidor\\software\\Contabilidad", "destinos": { "Fernando Alonso": "\\\\PC66\\c$\\Users\\Fernando\\Contabilidad", "Servidor Contabilidad": "\\\\SRVCONTA\\Contabilidad" } } } Básicamente se introduce un nombre para el grupo de copia, por ejemplo "FACTURACION" y a continuación se establece un origen y los destinos de copia. En el ejemplo anterior se copiarán los ficheros de la carpeta:
a los equipos:
Y también se copiará el contenido de la carpeta:
a los equipos:
Como vemos este ejemplo que mostraremos a continuación puede ser útil para actualizar aplicaciones de escritorio cliente/servidor pues permite especificar varios orígenes (varias carpetas) y varios destinos a los que copiar los orígenes. Además de que gracias al método asíncrono, el script copiará varios ficheros a la vez (hasta el límite establecido por concurrency). Este script en JavaScript también realizará un ping inicial a cada destino de copia para comprobar si está disponible. Dicho ping también lo lanzará en modo asíncrono, lanzando varios ping a la vez, hasta el límite establecido por concurrency.
Código JavaScript para copiar carpetas a varios destinos de forma asíncronaA continuación mostramos el código del script completo en JavaScript que permitirá leer el fichero JSON de configuración para copiar los ficheros de las carpetas origen a los destinos especificados. Además de realizar un ping a todos los equipos destino para verificar su disponibilidad: El siguiente código puede descargarse gratuitamente pulsando aquí: var colors = require('colors'); var pad = require('pad'); var path = require('path'); var Promise = require("bluebird"); var fs = Promise.promisifyAll(require("fs")); var ping = require('ping-bluebird'); var Dict = require("collections/dict"); var mkdirp = require("mkdirp-then"); var config = require('./actualizar.json'); //Array donde se registran los ficheros en uso var ficherosEnUso = []; //Diccionario [host, bool] que almacena para cada //servidor destino el estado de conexión var hostsDestino = new Dict(); //Obtiene el agrumento que identifica la aplicación //a actualizar. Si no se especifica actualiza todos los //que aparezcan en el fichero de configuración var appParaActualizar = null; if (process.argv.length > 0) { var arg0 = process.argv[0].toLowerCase(); //Si se ejecuta con node.exe, el segundo //parámetro es el nombre del .js y el tercero //el nombre de la aplicación a actualizar if (arg0.indexOf("node") > -1 && process.argv.length > 2) appParaActualizar = process.argv[2]; //Si se ejecuta utilizando el .js //empaquetado en un ejecutable con JX, el segundo parámetro //es el nombre de la aplicación a actualizar else if (arg0.indexOf("node") == -1 && process.argv.length > 1) appParaActualizar = process.argv[1]; } if (appParaActualizar != null) { console.log("Actualizando " + appParaActualizar); } else { console.log("Actualizando todas las aplicaciones."); } //Comprueba la conectividad de los destinos con ping Promise.map(Object.keys(config), function (app) { if (appParaActualizar == null || appParaActualizar == app) { console.log("\nComprobando conectividad para %s:" . black.bgWhite, app); var destinos = []; var destinosApp = config[app]['destinos']; for (var key in destinosApp) { var destino = destinosApp[key].toLowerCase(); //Se comprueba la conectividad de los servidores de los destinos //Los destinos locales no los comprueba (P.ej: c:) if (destino.indexOf(":") == -1) { var host = destino.replace("\\\\", "").split('\\')[0]; destinos.push(destino); if (!hostsDestino.has(host)) { hostsDestino.add(false, host); } } } //Realiza un Ping de forma asíncrona para cada servidor destino return Promise.map(hostsDestino.keys(), function (host) { return ping(host, { timeout: 1 }).then(function (res) { var respuesta = res.alive ? " conectado ".green : " desconectado ".red; console.log(pad(res.host, 15) + " " + respuesta); hostsDestino.set(res.host, res.alive); }).reflect(); }); } }, {concurrency: 20}) .then(function (object) { //Copia a los destinos que tienen conectividad return Promise.map(Object.keys(config), function (app) { if (appParaActualizar == null || appParaActualizar == app) { console.log("\nIniciando la copia para %s:".black.bgWhite, app); var destinos = []; //Array de rutas de los destinos var destinosApp = config[app]['destinos']; for (var key in destinosApp) { var destino = destinosApp[key].toLowerCase(); //Si es una copia local añade el destino. P.e: c:\... if (destino.indexOf(':') > -1) { destinos.push(destino); } else { //Añade la ruta del destino si hay conectividad //con el servidor de destino var host = destino.replace("\\\\", "").split('\\')[0]; if (hostsDestino.get(host, false)) { destinos.push(destino); } } } if (destinos.length > 0) { // Lanza el proceso de copia de la applicación a los destinos return copiarDirectorio(config[app].origen, config[app].origen, destinos); } } }, { concurrency: 100 }); }).then(function () { console.log("\nResultado:".black.bgWhite); //Imprime la información sobre la conectividad //de los destinos de las copias en pantalla if (hostsDestino.values().some(function (conectado, index) { return conectado; })) { console.log("\nDestinos con conectividad:".bold); hostsDestino.forEach(function (conectado, servidor) { if (conectado) console.log(pad(" ", 4) + "%s", servidor); }); } if (hostsDestino.values().some(function (conectado, index) { return !conectado; })) { console.error("\nDestinos sin conectividad:".bold); hostsDestino.forEach(function (conectado, servidor) { if (!conectado) console.error(pad(" ", 4) + "%s", servidor); }); } //Muestra los ficheros que no se han copiado debido //a que estaban en uso if (ficherosEnUso.length > 0) { console.error("\nFicheros en uso:".bold); ficherosEnUso.forEach(function (ruta) { console.error(pad(" ", 4) + "%s", ruta); }); } }); // Funciones auxiliares /** * Función que lee el contenido de un directorio origen * recursivamente y lo copia en uno o varios destinos, * todo de forma asíncrona. * @param directorio Directorio actual cuyos ficheros se * copian a los destinos. Inicialemnte es el mismo que * el origen. Conforme va recorriendo recursivamente * los subdirectorios este parámetro cambia indicando * el directorio actual que esta copiando. * @param origen Directorio raíz de la aplicación que * se copia. No varía en cada iteración recursiva. * @param destinos Array de rutas de las carpetas de * destino. No varía en cada iteración recursiva. * @returns {Function} */ function copiarDirectorio(directorio, origen, destinos) { //Lee el contenido del directorio. Por cada fichero //encontrado ejecuta lo siguiente return fs.readdirAsync(directorio).map(function (fichero) { var ruta = path.join(directorio, fichero); //Obtiene la información del fichero/directorio return fs.statAsync(ruta).then(function (informaciónFichero) { //Si es directorio lo copia recursivamente if (informaciónFichero.isDirectory()) return copiarDirectorio(ruta, origen, destinos); else { //Si es fichero lo carga en memoria console.log(pad("Leyendo", 15).yellow + ruta.replace(origen, '').gray); return fs.readFileAsync(ruta).catch(function ignore(err) { }) .then(function (contenido) { //Escribe de forma asíncrona el fichero actual //en cada destino return Promise.map(destinos, function (prefijoDestino, index, length) { //Anida la ruta relativa del fichero que está //copiando a la ruta absoluta de la raíz del destino var destino = path.join(prefijoDestino, ruta.replace(origen, '')); //Crea el directorio de destino si no existe return mkdirp(path.dirname(destino)).then(function () { console.log(pad("Escribiendo", 15).green + destino); //Escribe el fichero cargado en memoria en el destino return fs.writeFileAsync(destino, contenido).then( function onFulfilled(value) { return { state: 'fulfilled', value: value }; }, function onRejected(reason) { if (reason.code == "EBUSY") { ficherosEnUso.push(reason.path); } else { console.error("%s", reason.message); } //Ignora el rechazo devolviendo fulfilled return { state: 'fulfilled', value: false }; }); }, function onRejected(reason) { console.error("%s no ha podido ser creada", reason.path); //Ignora el rechazo devolviendo fulfilled return { state: 'fulfilled', value: false }; }); }); }, function onRejected(reason) { console.error("%s no ha podido ser leido." + reason.path); //Ignora el rechazo devolviendo fulfilled return { state: 'fulfilled', value: false }; }); } }); }, {concurrency: 20}); } process.on('uncaughtException', function (err) { console.error((new Date).toUTCString() + ' Excepción no controlada: '.red, err.message); }); process.on('unhandledRejection', function (reason, p) { if (reason.code == "EBUSY") { console.error(reason.path + " está en uso."); } else { console.error("Error: " + reason.message); } }); Será suficiente con copiar el código anterior en un fichero de texto plano sin formato y llamarlo, por ejemplo, actualizar.js.
Cómo interpretar y ejecutar JavaScript mediante Node.jsUna vez creado el fichero JavaScript como hemos indicando anteriormente, ahora tendremos que ejecutarlo para que realice su función. A continuación explicamos los pasos a seguir para interpretar o ejecutar un fichero JavaScript desde fuera de un navegador, desde nuestro equipo de escritorio, como si se tratara de una aplicación o comando Windows. Para ello usaremos la herramienta Node.js que es de libre distribución y sirve, entre otras cosas, para interpretar código JavaScript desde fuera del navegador. Qué es Node.jsAntes de continuar con nuestro script vamos a aportar un poco de teoría sobre Node.js, que nos ayudará a entender para qué sirve y cuáles son sus características. Node.js es un entorno en tiempo de ejecución multiplataforma, de código abierto, para la capa del servidor (pero no limitándose a ello) basado en el lenguaje de programación ECMAScript, asíncrono, con I/O de datos en una arquitectura orientada a eventos y basado en el motor V8 de Google. Fue creado con el enfoque de ser útil en la creación de programas de red altamente escalables, como por ejemplo, servidores web. Node.js es similar en su propósito a Twisted o Tornado de Python, Perl Object Environment de Perl, React de PHP, libevent o libev de C, EventMachine de Ruby, vibe.d de D y de Java existe Apache MINA, Netty, Akka, Vert.x, Grizzly o Xsocket. Al contrario que la mayoría del código JavaScript, no se ejecuta en un navegador, sino en el servidor. Node.js implementa algunas especificaciones de CommonJS. Node.js incluye un entorno REPL para depuración interactiva. Algunos aspectos técnicos interesantes de Node.js:
Instalar Node.js en un equipo WindowsEn primer lugar descargaremos el instalador apropiado para nuestro sistema operativo de la web oficial de Node.js, para ello accederemos a la URL:
Y descargaremos la versión estable, en nuestro caso "v5.0.0 Stable": Guardaremos el fichero node-v5.0.0-x64.msi: Ejecutaremos el fichero descaragado: Es posible que nos muestre un mensaje de advertencia de seguridad, pulsaremos "Ejecutar": Se iniciará el asistente de instalación de Node.js, pulsaremos "Next": Leeremos los términos de licencia, si estamos de acuerdo marcaremos "I accept the terms in the Licence Agreement" y pulsaremos "Next": Pulsaremos "Next": Se instalarán las siguientes herramientas:
Pulsaremos "Install" para iniciar la instalación de Node.js: El asistente nos indicará que Node.js ha sido instalado correctamente en nuestro equipo:
Crear fichero JSON package.json de configuración del paquete con Node.jsUna vez instalado Node.js en nuestro equipo podremos usar sus comandos para interpretar y ejecutar código JavaScript. Antes de ejecutar nuestro script de ejemplo JavaScript actualizar.js deberemos obtener todas las librerías que hayamos necesitado (incluidas en la cláusula require) y crear el fichero package.json, necesario para la ejecución de un fichero JavaScript con Node.js. Para crear el fichero package.json usaremos Node.js, abriremos una ventana de MS-DOS (línea de comandos de Windows), para ello pulsaremos en el botón Inicio y escribiremos "cmd", nos mostrará la herramienta de shell, pulsaremos con el botón derecho del ratón sobre ella y seleccionaremos "Ejecutar como administrador": Ahora nos posicionaremos en la carpeta donde tengamos el fichero actualizar.js, en nuestro caso ejecutaremos los siguientes comandos MS-DOS:
Una vez posicionados en la carpeta con el fichero actualizar.js ejecutaremos el siguiente comando usando el paquete "npm":
Nos pedirá una serie de datos (podemos dejar todos los de defecto pulsando INTRO), nos pedirá el nombre de la aplicación (name), la versión (version), la descripción (description), punto de entrada (entry point) que pode defecto suele ser el nombre del fichero .js, palabras clave, autor, licencia, etc. Introduciremos cada dato y pulsaremos INTRO. Al finalizar la captura de datos nos preguntará si es correcto (Is this ok?), pulsaremos INTRO: El comando anterior nos habrá creado el fichero package.json:
Descarga de las dependencias, librerías adicionales con Node.jsSi nuestro script JavaScript incluye dependencias (require) deberemos especificarlas en el fichero package.json y descargar los ficheros de cada dependencia. Para hace esto de forma automática podemos usar Node.js. En primer lugar consultaremos las dependencias que hemos usado en nuestro .js, en nuestro caso:
y ejecutaremos el siguiente comando (siempre en la carpeta donde se encuenta el fichero JavaScript):
Si todo es correcto Node.js con el paquete npm descargará en una subcarpeta de nuestro proyecto todos los ficheros necesarios de cada librería o paquete usado por nuestro fichero JavaScript, mostrando el siguiente resultado: Además, el comando anterior habrá editado el fichero package.json y habrá añadido las dependencias correspondientes. También habrá creado la subcarpeta "node_modules" con los ficheros de cada librería referenciada. Podemos consultar el contenido del fichero package.json que puede abrirse con cualquier editor de texto plano sin formato: Vemos que ha añadido "dependencies" y todas las librerías de los requiere del fichero JavaScript que hayamos indicado que se descarguen con el comando anterior: El contenido de la subcarpeta "node_modules":
Ejecutar JavaScript con Node.jsUna vez que hemos añadido las dependencias al fichero package.json y hemos descargado los ficheros .js necesarios para estas dependencias, como hemos indicado en pasos anteriores, podremos ejecutar nuestro script. Para ello usaremos el siguiente comando (siempre en el directorio donde tenemos el script .js, el fichero package.json y los ficheros de dependencias):
Un ejemplo de salida de la ejecución de nuestro script actualizar.js: Como vemos el script primero comprueba la disponibilidad de los equipos destino, haciendo un ping. Si no responde no intenta la copia, si responde intenta realizar la copia. Al finalizar el script muestra el resultado de la copia, los destinos que han tenido conectividad y los que no.
Cómo generar un ejecutable a partir de un JavaScript con JXcore y Node.jsEl proceso anterior puede ser un poco tedioso si queremos ejecutar nuestro JavaScript en otros equipos, tendríamos que instalar Node.js y usar el comando "node xxx.js" para ejecutar el script. Para evitar esto, existe una herramienta gratuita llamada JXcore que permite encapsular todo lo necesario en un ejecutable para que nuestro script puede ejecutarse en cualquier PC sin necesidad de instalar software adicional. A continuación vamos a explicar cómo generar un fichero ejecutable a partir de un fichero JavaScript. Para ello necesitaremos tener instalado en nuestro equipo Node.js, como indicamos aquí y también necesitaremos JXcore. JXcore es una herramienta que a partir de Node.js permite generar un ejecutable embebido con todo lo necesario para que nuestra aplicación JavaScript pueda funcionar en cualquier PC sin necesidad de instalar Node.js ni ningún otro software. Con JXcore generaremos un .exe (ejecutable) a partir del .js (JavaScript). Para descargar e instalar JXcore accederemos a la URL:
En nuestro caso pulsaremos "Download" en Windows Setup (32/64/SM/V8): Guardaremos el archivo jx_winsetup.zip: Lo descomprimiremos: Ejecutaremos el fichero JXcore_setup.exe: Se iniciará el asistente de instalación de JXcore, pulsaremos "Next": Leeremos los términos de liciencia, si estamos de acuerdo marcaremos "I accept the agreement" y pulsaremos "Next": Elegiremos la carpeta de instalación y pulsaremos "Next": A continuación podremos elegir el ambiente de ejecución a instalar, en nuestro caso "V8 x64": Elegiremos el nombre para la carpeta que se creará en la barra de tareas y pulsaremos "Next": Es recomendable marcar la opción "Add application directory to environment PATH variable" para poder ejecutar el comando "jx" desde cualquier carpeta del equipo: Comprobaremos las opciones elegidas, si son correctas puldaremos "Install" para instalar definitivamente JXcore: Tras la instalación el asistente nos indicará que JXcore ha sido instalado correctamente. Pulsaremos "Finish": Una vez instalado JXcore ya podremos usar el comando "jx" para generar el ejecutable a partir del fichero JavaScritp y sus dependencias. Para ello abriremos una ventana de MS-DOS (como hemos explicado anteriormente) y accederemos a la carpeta donde tengamos el fichero de scritp JavaScript, en nuestro caso:
Una vez en la carpeta ejecutaremos el siguiente comando:
Si todo es correcto y se ha generado el fichero ejecutable .exe a partir del .js nos mostrará el mensaje [OK] compiled file is ready (C:\AjpdSoft_JavaScript\actualizar.exe). Y podremos comprobar que en la carpeta del script JavaScript se ha generado el fichero actualizar.exe que ahora podrá funcionar en cualquier equipo sin necesidad de instalar Node.js ni de descargar las librerías adicionales pues van embebida en el ejecutable: Y como decimos, podremos ejecutar el fichero actualizar.exe que se comportará como el fichero actualizar.js y sus librerías dependientes. En el caso de nuestro script de ejemplo podremos pasarle como parámetro al ejecutable el nombre del grupo de origen y destinos a utilizar que viene dado en el fichero actualizar.json: El contenido del fichero actaulizar.json de ejemplo del que ha leído la información el fichero actualizar.exe:
AnexoC:/AjpdSoft_JavaScript>npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install
Artículos relacionados
CréditosIdea original y código fuente JavaScript realizado por Alejandro Centenero para Proyecto AjpdSoft. Anuncios
Enviado el Miércoles, 18 noviembre a las 20:52:13 por ajpdsoft
|
|