DEV Community

loading...

Minio como alternativa ao S3 em aplicações Node

Júnior Mendes
I'm a Brazilian back-end developer, with some front-end and devops powers. I do not play soccer well :(
・4 min read

A facilidade em criar buckets e enviar arquivos para o S3 da Amazon é algo fantástico. Armazenar objetos, ao invés de ter um sistema de arquivos, diminui a complexidade e traz uma eficiência à esse tipo de operação.

Um dos problemas da Amazon é o seu valor. Em projetos menores, que não se beneficiam da grande disponibilidade da plataforma, é comum adotar - pelo menos para hospedar a aplicação - a Digital Ocean. Nesse artigo, vamos utilizar uma "s3 própria" por meio do Minio em uma API básica para upload de arquivos, feita com Node.js e Javascript.

Curte uma trilha sonora? Eu fiz esse artigo ouvindo essa playlist no spotify.

O que é o Minio?

Segundo a própria documentação, o Minio é uma tecnologia de armazenamento baseada em objetos, de alta performance. Ele é escrito em Go e tem uma API compatível com o AWS S3.

Minio Docs page

Vale destacar a questão da compatibilidade. Como veremos, é possível até configurar o cliente do S3 da SDK para Javascript de modo que ele utilize, na verdade, o minio.

Criando a API

Aqui, suponho que você possua um projeto configurado para utilizar ES6 Modules - uma forma simples é por meio do Babel. Caso precise, utilize este template.

O primeiro passo é criar uma API para que possamos testar, no futuro, a funcionalidade. Utilizando express, teríamos algo assim:

import express from 'express';
import { S3 } from 'aws-sdk';
import cors from 'cors';
import morgan from 'morgan';

const app = express();
app.use(cors());
app.use(morgan('dev'));

app.listen(3000, () => console.log('[READY]')); 
Enter fullscreen mode Exit fullscreen mode

Depois disso, é necessário criar uma rota que trate o upload de arquivos individuais. Aqui faremos uso do Multer:

app.route('/upload').post(
  multer({ storage: multer.memoryStorage() }).single('file'),
  uploadHandler,
);
Enter fullscreen mode Exit fullscreen mode

Upload Handler

É nessa função em que utilizamos a API da S3:

const uploadHandler = async (req, res) => {
  try {
    await s3.putObject({
      Bucket: 'tests',
      Key: req.file.originalname,
      Body: req.file.buffer,
    }).promise();
    return res
      .status(201)
      .send({
        message: `File "${req.file.originalname}" uploaded`
      });
  } catch (e) {
    console.log(e);
      return res
        .status(500)
        .send({ error: e.message })
  }
}
Enter fullscreen mode Exit fullscreen mode

E assim criamos o objeto s3:

const s3 = new S3({
  endpoint: 'http://play.minio.io:9000',
  accessKeyId: 'Q3AM3UQ867SPQQA43P2F',
  secretAccessKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG',
  sslEnabled: false,
  s3ForcePathStyle: true,
});
Enter fullscreen mode Exit fullscreen mode

Nesse caso, as credenciais são para o servidor de testes do Minio.

Por que não usar a SDK do Minio para JS? Diga-se de passagem, por design, não existe muita diferença, mas aqui estou fazendo isso para mostrar como adaptar, sem basicamente nenhuma mudança na estrutura do projeto, o serviço utilizado. Isso também é útil para utilizar pacotes como o multer-s3.

Dessa forma, o código completo seria o seguinte:

import express from 'express';
import { S3 } from 'aws-sdk';
import cors from 'cors';
import morgan from 'morgan';

const s3 = new S3({
  endpoint: 'http://play.min.io:9000',
  accessKeyId: 'Q3AM3UQ867SPQQA43P2F',
  secretAccessKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG',
  sslEnabled: false,
  s3ForcePathStyle: true,
});

const app = express();
app.use(cors());
app.use(morgan('dev'));

const uploadHandler = async (req, res) => {
  try {
    await s3.putObject({
      Bucket: 'tests',
      Key: req.file.originalname,
      Body: req.file.buffer,
    }).promise();
    return res
      .status(201)
      .send({
        message: `File "${req.file.originalname}" uploaded`
      });
  } catch (e) {
    console.log(e);
      return res
        .status(500)
        .send({ error: e.message })
  }
}

app.route('/upload').post(
  multer({ storage: multer.memoryStorage() }).single('file'),
  uploadHandler,
);

app.listen(3000, () => console.log('[READY]')); 
Enter fullscreen mode Exit fullscreen mode

Utilizando a API própria do Minio

Caso você não precise utilizar a API da S3, algumas alterações seriam necessárias. A primeira é a própria definição do objeto "s3". Poderíamos substituir pelo seguinte:

const minioClient = new Minio.Client({
    endPoint: 'play.min.io',
    port: 9000,
    useSSL: true,
    accessKey: 'Q3AM3UQ867SPQQA43P2F',
    secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG'
});
Enter fullscreen mode Exit fullscreen mode

A segunda seria na utilização desse cliente para realizar o upload, dentro da função uploadHandler, para a seguinte forma:

//...
await minioClient.putObject(
  'tests',
  req.file.originalname,
  req.file.buffer,
);
Enter fullscreen mode Exit fullscreen mode

Considerações

Caso você execute o código criado, é possível realizar upload de arquivos por meio de requisições http para localhost:3000/upload utilizando multipart/form-data. O multer faz o trabalho pesado de modo que temos total acesso ao arquivo na memório (atravez do memoryStorage).

Dito isso, utilizamos a SDK da AWS da maneira convencional, contudo o objeto que nos dá acesso à API da S3 é criado com alguns parâmetros importantes - sobretudo o endpoint, que recebe a URL do servidor minio de interesse. Observe que quase nenhuma alteração é necessária, uma vez que o Minio foi estruturado para permitir esse tipo de compatibilidade.

Referências

Discussion (0)