¿Comó subir imagenes base64 a amazon S3 con nodeJs y Sails?


Hola amigos ya llevamos varios tutoriales hablando sobre sails Js para autenticar ususarios con redis y tokens, registro de usuarios y actualizar datos del perfil.

para esto vamos a seguir trabajando en el mismo repositorio https://github.com/herel/api-login

1.- ¿Cómo crear variables de entorno?

Como vimos en los tutoriales anterirores configuramos nuestro archivo .env que contiene las variables de entorno para poder ejecutar una nueva api sin tener que mover mucho codigo debemos agregar a nuestras variables  accessKeyId, secretAccessKey y Bucket. yo las agregue con los siguientes nombres:

MAIN_DB_ADAPTER="sails-mongo"
MAIN_DB_HOST="localhost"
MAIN_DB_PORT="27017"
MAIN_DB_DB="********"
TOKEN_KEY="********"
REDIS_PORT=6379
REDIS_HOST="localhost"
REDIS_DB=1
DOMAIN_APP="http://herelodin.com"
DOMAIN_API="http://api.herelodin.com/v1.0/"

MAIN_S3_KEY="**********DZ3LY2A"
MAIN_S3_KEY_SECRET="*************vZLQ5hr0*******"
MAIN_S3_BUCKET="*******"

deberan configurar AMAZON S3 con sus respectivas llaves.

2.- ¿Cómo crear un route en SailsJs?

Como vimos en otros tutoriales decidimos deshabilitar blueprints,actions entre otros, por lo que tenemos que crear la url (end point) donde vamos  a subir la imagen en BASE64 para esto vamos al archivo config/routes.js y agregamos el siguiente codigo

module.exports.routes = {

  '/': {
    view: 'pages/homepage'
  },
  'POST /v1.0/account' : 'AccountController.create',
  'POST /v1.0/account/login' : 'AccountController.login',
  'PUT /v1.0/account'        : 'UserController.update',
  'POST /v1.0/media'         : 'MediaController.upload'

};

Como podemos observar creamos la url de tipo POST llamada /v1.0/media donde ejecutaremos la funcion upload del archivo MediaController vamos.

3.- ¿Cómo crear un servicio en sails?

Ya tenemos configuradas las variables de entorno y el end point que recibe la imagen ahora debemos crear un servicio que  se encargara de recibir la imagen en base64 convertirla a un archivo y subirla en amazon s3.

Debemos crear el archivo api/services/UploadService.js con el siguiente codigo:

var aws = require('aws-sdk');

aws.config.update({
    accessKeyId         : process.env.MAIN_S3_KEY,
    secretAccessKey     : process.env.MAIN_S3_KEY_SECRET
});

var s3 = new aws.S3();


module.exports = {
    base64 : function(base64,fileName){
        return new Promise(function (resolve, reject){
            var decodedImage = new Buffer(base64.replace('data:image/png;base64,',''), 'base64');
            s3.upload({
                Bucket: process.env.MAIN_S3_BUCKET,
                Key: fileName,
                Body: decodedImage
            }, function (err, data) {
                if(err)
                    return reject(err);
                return resolve(data);
            });
        });
    }
}

en este pequeño codigo inicializamos amazon que previamente instalamos con el comando:

sudo npm install aws-sdk --save

agregamos el accessKeyId y  secretAccessKey y creamos un objeto nuevo con la configuracion de amazon s3 lista para subir archivos.

tambien creamos una función llamada base64 que recibe dos  parametros el string con la imagen y el nombre del archivo, creamos un promise para notificarle cuando se termine de subir el archivo o ocurra un error  y convertimos el string en un archivo blob de tipo imagen con la función new Buffer

4.- ¿Cómo crear un controller en sailsJs?

hasta este punto ya tenemos:

  • end point donde recibimos la imagen en base64
  • servicio que se encarga de subir la imagen a amazon s3

No hace falta el controller que indicamos en el archivo routes.js para esto creamos un controller en api/controllers/MediaController con la función upload.js

var moment  = require('moment');
module.exports = {
    upload : function(req,res){
        var params = req.allParams();
        var date   = moment().format('DD-MM-YYY-hh-mm-ss');
        if(!params.image)
            return res.send(412, { error : true, message : "image is required", status : 412 });
        async.waterfall([
            function upload(cb){
                UploadService.base64(params.image,`${date}.png`)
                    .then(function($result){
                        return cb(null,$result);
                    }).catch(cb)
            }
        ], function done(err,result){
            if (err && err.status) return res.send(err.status, err);
                else if (err)
                    return res.send(500, {
                        error: true,
                        message: "Internal server error",
                        status: 500
                    });
                return res.json(result);
        });
    }
}

¿Que hace este pequeño codigo?

  • importamos moment que vamos a utlizarlo para crear el nombre del archivo con la fecha actual dia-mes-año-hora-minutos-segundos.png
  • validamos que viene la imagen en los parametros
  • mandamos a llamar el servico y le pasamos el string con la imagen base64 y el nombre temporal
  • si todo sale bien respondemos el objeto que mando amazon con la url
  • Si algo sale mal mostramos el error correspondiente

5.- Pruebas con postman

vamos a probar el endpoint con postman para esto busquemos una imagen y la convertimos a base64

5.1- validando errores

Si no enviamos la imagen nos debe responder el siguiente error

5.2- subiendo imagen

Si agregamos el atributo image con la data obtenemos el siguiente mensaje

pueden probar el end point aqui http://api.herelodin.com/v1.0/media  o descargar el repositorio

¡Genial ! la imagen se subio con exito y amazon s3 nos respondio la url que asigno https://momazos.s3.amazonaws.com/19-05-182018-12-48-22.png  pero que podemos hacer con esto.

  1. Si agregamos un policie podemos saber que usuario hizo el request y actualizar su foto de perfil y en vez de responder la imagen respondemos un objeto con el usuario actualizado y la nueva imagen de perfil
  2. si tenemos un formulario con varios campos y 1 con uno o varios archivos podemos ir subiendo las imagenes mientras el usuario llena el formulario y al final enviar los datos con las urls de las imagenes.

En nuestro siguiente post vamos a realizar un ejemplo para actualiza la foto de perfil del usuario logueado y en vez de subir  base64 subir uno de tipo file.

Gracias por compartir y comentar, Aquí algunos libros que me han ayudado mucho en mejoras las buenas practicas para programar.