Parte 4: Carga de imágenes
Hasta ahora, la parte 0 y la parte 1 cubren el modelo de datos y el documento de usuario utilizado en la aplicación, seguido de la parte 2 donde verificamos cuentas a través de correo electrónico utilizando Nodemailer y Sendgrid. Luego tuvimos la parte 3 que cubrió la autenticación de usuario basada en sesión. Ahora que podemos crear un usuario, y permitirle iniciar sesión, sería ideal asociar una foto de perfil a este usuario. Para ello, debemos ser capaces de subir imágenes, y recortarlas para que todas tengan una forma uniforme. Empecemos.
Materiales necesarios:
- Node.js
- Express
- Biblioteca 'fs' de Node.js
- Binarios GraphicsMagick
Módulos de nodo utilizados:
- Couchbase Node.js SDK/N1QL
- body-parser
- uuid
- Multer
- GraphicsMagick
Resumen:
Una de las partes más difíciles de construir Touchbase fue crear una forma de almacenar imágenes. Esto fue una lucha porque hay muchas partes diferentes (front-end y back-end) que deben unirse para este proceso. Era necesario recortar las imágenes para que los usuarios tuvieran el máximo control sobre ellas. Esto requirió un módulo de recorte para Angular.js, ng-cropperasí como una forma de cambiar el tamaño de la imagen a las nuevas dimensiones en el back-end proporcionado por graphicsMagick. También era necesario elegir en qué formato almacenar estas imágenes (base64), entre muchos enfoques diferentes. Esta entrada del blog cubre el proceso de pasar la imagen de front-end a back-end, incluyendo el recorte y el formato.
Instalación de Multer y GraphicsMagick
La configuración del módulo Multer es una parte importante de este proceso. Multer es un módulo de nodo que utilizamos para almacenar las imágenes que se publican desde el front-end, en una carpeta de nuestro servidor. Para descargar el módulo, ejecute el siguiente comando.
1 |
$ npm instale multer --guardar |
Otro módulo importante que necesitamos es GraphicsMagick. La descarga de GraphicsMagick debe hacerse en dos partes: instalar los binarios de GraphicsMagick en tu sistema, y descargar el módulo node GraphicsMagick. Esta combinación nos permite alterar imágenes en una ruta de archivo especificada dentro de nuestra aplicación Node.js. Las instrucciones para descargar los binarios GraphicsMagick para los sistemas Linux, OSX y Windows se pueden encontrar en la página web Repo Github de Touchbase. Una vez completado este paso, descargue el módulo node para GraphicsMagick con el siguiente comando.
1 |
$ npm instale gm --guardar |
Configuración del multer
La configuración del módulo Multer se realiza en el módulo app.js y ayudará a indicar en qué fase del proceso de carga se encuentra una imagen. La parte que más nos interesa es cuándo se completa la carga de una imagen, lo que se indica mediante una variable llamada Hecho. Una vez que esto ocurra, podremos acceder con seguridad al archivo y realizar el recorte y la reducción de tamaño necesarios de la imagen.
app.js Configuración de Multer
Hay un punto final de la API que se encarga de recortar y reducir el tamaño de la imagen (utilizando la configuración que hicimos anteriormente), y la mayor parte de su trabajo se produce en el archivo "Imagen.intento función. Esta función sólo se ejecuta una vez finalizada la carga de la imagen (cuando hecho es verdadero), y Multer lo ha almacenado en el archivo carga especificada en la carpeta app.js configuración. La propia ruta sólo puede recuperar unos pocos atributos de la imagen antes de que se complete la carga de la imagen. Estos metadatos que Multer recibe serán cruciales durante el proceso de recorte.
Multer simplemente carga cualquier tipo de formulario que sea del tipo <enctype=’multipart/form-data’>. La imagen es simplemente un tipo de entrada en este formulario, por lo que también podemos añadir otros atributos al formulario. Por ejemplo, para obtener los datos de recorte de ng-cropper, creo un atributo oculto del formulario que es un objeto que contiene todos los datos de recorte. De esta forma, la imagen y los metadatos del front-end se envían juntos en el HTML POST que se ejecuta cuando se envía el formulario. Durante este proceso, también accedo al localStorage del navegador, que contiene el sessionID (discutido en la parte 3) para enviar el ID, de modo que la ruta pueda ser autenticada. El Sesión.auth se ajusta para manejar esta situación en la que el identificador de sesión aparece en el campo req.body (lo que ocurre cuando se envía el sessionID como metadatos Multer). Para ver cómo se hace, a continuación se muestra la implementación en Angular.js de ng-cropper, así como el formulario HTML que carga la imagen con los datos de recorte.
Formulario HTML con POST en public/html/picture-partial.html
Uso de 'ng-cropper' en pictureController dentro de public/js/touchbase.js
Multer divide los datos del formulario en dos partes diferentes cuando se envían al back-end. Todos los metadatos sobre los archivos cargados se especifican en req.files. Esto nos dice cosas básicas como la ruta de archivo de la imagen, el tamaño de la imagen, el formato de la imagen, etc. Diferentes aspectos de esto se utilizan en nuestro "Imagen.intento función. El resto de los datos del front-end, todo lo que no sea un fichero, se encuentra en la función req.body atributo. Desde aquí, podemos acceder a los datos de recorte que hemos cargado utilizando el atributo HTML del formulario. La forma de acceder a estos diferentes parámetros es utilizando los atributos de las diferentes partes del formulario. Por ejemplo, al acceder a la subida de la foto, en el formulario front-end teníamos la entrada de fichero con un nombre de FotoUsuario. Para acceder a los metadatos correspondientes, buscaríamos en req.files.userPhoto. Para obtener los datos del cultivo, nos fijaremos en su nombre, cropDimy acceder a él mediante 'req.body.cropDim'. A estos datos se accede en el /api/uploadAttempt ruta, que puede verse a continuación.
API '/api/uploadAttempt
Ahora que podemos acceder a todos estos datos, tenemos que utilizarlos. Se utilizan en el "Imagen.intento función en models/picturemodel.js donde podemos usar esto para alterar realmente la imagen. En primer lugar, debemos comprobar que esta imagen es algo que podemos almacenar correctamente. En nuestra configuración de Multer en app.js permitimos subir imágenes de hasta 20mb. Hacemos esto por propósitos de manejo de errores, permitiendo mucho más de lo que la mayoría necesitaría. Si se sube un archivo con un tamaño superior a nuestro límite, Multer falla silenciosamente, por lo que simplemente establecemos un límite aribitariamente alto. En "Imagen.intentoComprobamos si la imagen pesa más de 7,5 MB y, en caso afirmativo, la eliminamos. En este segundo nivel de validación, informaríamos al usuario de que el tamaño máximo del archivo es de 7,5 mb y que su imagen no ha podido cargarse, indicándole cómo puede solucionar el problema. El sitio 'fs.unlink' borra el archivo usando la ruta que Multer nos dio en el comando req.files atributo. A continuación, la función llama a "Imagen.intentocon la ruta de archivo mencionada y manipula la imagen con la función fs y GraphicsMagick.
Función "Picture.attempt
Los pasos finales de validación de la imagen provienen de GraphicsMagick. La razón por la que usamos GraphicsMagick para comprobar el tipo de archivo en lugar de Multer es porque Multer sólo comprueba la extensión del archivo, que no representa necesariamente el contenido. GraphicsMagick hace su propio análisis del archivo. Si el tipo de archivo no es uno de los formatos aceptables, la carga falla y el usuario es notificado del error. Pasando esta validación, sólo necesitamos alterar el archivo para recortarlo y reducir su tamaño.
Utilizando GraphicsMagick, el primer paso es 'autoOrient()' que utilizará los datos EXIF (un estándar de formato de archivos de imagen) para orientar correctamente la imagen a la orientación en la que fue tomada. Esto es importante para las subidas desde móviles, ya que muchas de ellas pueden girar 90 grados, dando lugar a una imagen extraña.
A continuación, la imagen se recorta utilizando los datos de recorte que obtuvimos del front-end a través de 'req.body.cropDim'. Esto se pasa entonces a la función de recorte de GraphicsMagick, que requiere un desplazamiento X e Y, así como la altura y anchura de la imagen. ng-cropper espera este método de recorte y nos da estos atributos cuando la imagen es recortada haciendo que sea sencillo para nosotros simplemente pasar estos atributos directamente a GraphicsMagick.
Por último, reducimos el tamaño de la imagen para que el archivo sea más pequeño. En mi opinión, 7,5mb, o incluso 2mb es un poco grande para estar almacenando y recuperando cada vez que se utiliza el sitio web, y se ve un perfil de usuario. Aunque esto puede no ser necesariamente una gran carga para la aplicación, podría causar una mala experiencia para los usuarios, especialmente los usuarios móviles. Después de algunas pruebas, descubrí que reducir la escala a 200px y ajustar la calidad de la imagen a 50% tenía el máximo beneficio en términos de calidad de la imagen y tamaño del archivo. En mi implementación actual, las imágenes son cuadrados de 150px. Tenga esto en cuenta, ya que es posible que desee hacer la reducción de tamaño de manera diferente para su aplicación si decide mostrar las imágenes más grandes o más pequeñas. Después de todos estos cambios, esta imagen alterada se escribe sobre su ubicación anterior.
Después de que la imagen esté correctamente alterada, queremos leerla de nuevo para transformar el binario de la imagen en una cadena base64 usando un Javascript Buffer . La razón por la que hacemos esto es porque HTML puede leer base64 en sus etiquetas de imagen, por lo que esto simplificará la representación de las imágenes en el front-end cuando queramos mostrarlas.
Por último, cargamos esta cadena base64 utilizando una operación de inserción Clave-Valor. Utilizando la Sesión.auth y el identificador de sesión enviado en los datos del formulario de Multer, conocemos el identificador del usuario que subió la imagen. Entonces en req.userID como podemos ver el /api/uploadAttempt API. Este ID de usuario, al que se añade "_picMulterNode", se utiliza como ID del documento para la imagen, de modo que podamos vincular fácilmente al usuario con esta imagen en el futuro cuando necesitemos encontrar el perfil completo de un usuario.
Couchbase es inteligente, y reconoce que esta cadena base64 que se ha subido no es JSON, y la almacena como tipo binario. Después de este punto, usamos el 'fs.unlink' para eliminar la imagen del carga carpeta. Esto hace que la carpeta carga como un búfer y, a continuación, inserta el contenido base64 de la imagen en la base de datos, antes de eliminarlo de la carpeta carga carpeta. Finalmente, se ejecuta una consulta N1QL para cambiar el atributo booleano 'login.hasPicture' para el usuario en cuestión a true, desde su valor por defecto false. Esto asegurará que al perfil del usuario no se le aplique la foto de stock, y en su lugar se ejecute una búsqueda de la foto de perfil subida por el usuario.
Función "Picture.receive
Al final de toda esta carga de imágenes, también tiene que haber una forma de recibir la foto de perfil de cada usuario. Para ello, utilizamos la función Picture.receive función. Ésta se llama en el /api/advancedSearch API en rutas/routes.js porque este endpoint tiene que encontrar a cada usuario y su foto de perfil para mostrarla en el front-end. El sitio Picture.receive toma el documentID de un usuario como entrada y devuelve la cadena base64 de su imagen. Primero comprueba que el booleano 'login.hasPicture' es verdadero. Si no lo es, simplemente aplica la imagen por defecto al usuario. Si el atributo es verdadero, entonces toma el atributo 'uuid' del usuario y le añade '_picMulterNode' y luego hace un get KEY/VALUE estándar para recuperar la imagen del usuario, que almacenamos como una cadena base64. El código para esta función se puede ver arriba.
En resumen, utilizamos Multer para gestionar las cargas de archivos desde <enctype=’multipart/form-data’> formularios, y los almacenó en un carga en nuestro servidor. También utilizamos atributos ocultos dentro de este formulario para almacenar los datos de recorte de ng-cropper, y los añadimos al cuerpo de los datos enviados en Multer. En el back-end, usamos GraphicsMagick para recortar primero la imagen con los datos de recorte de ng-cropper, y redujimos el tamaño de la imagen para una mejor UX. Convertimos la imagen a base64, la almacenamos en Couchbase, y la guardamos con un documentID que está relacionado con el documentID del usuario. Por último, escribimos una función para recuperar la cadena de imagen base64 de un individuo, de modo que se pueda utilizar con facilidad dentro de un atributo de imagen HTML en el front-end.
Con esto concluye la parte de carga de imágenes de la serie de tutoriales de Touchbase. ¡Por favor, deje un comentario a continuación con cualquier comentario, y gracias por leer!