Tive o privilégio de ser chamado para palestrar no MongoDB São Paulo, foi o primeiro evento grande que eu participei.
Na minha apresentação eu mostrei o que são paradigmas, como eles são formados e os benefícios de sair da sua zona de conforto e abraçar uma nova tecnologia pra aumentar a produtividade e obter ótimos resultados.
No último post eu expliquei como instalar e configurar o nginx com upload e upload progress module. Agora vamos fazer nossa aplicação funcionar com o Carrierwave e Unicorn.
Configurando o nginx
Primeiro vamos alterar o arquivo de configuração do nginx da nossa aplicação, abra o arquivo /opt/nginx/conf/sites-available/app.com no seu editor de textos favorito e adicione o seguinte conteúdo:
upstream app_unicorn {
server unix:/tmp/unicorn.app.com.sock fail_timeout=0;
}
upload_progress proxied 1m;
server {
listen 80;
server_name arquivos.app.com www.arquivos.app.com;
if ($host = 'www.arquivos.app.com' ) {
rewrite ^/(.*)$ http://arquivos.arquivos.app.com/$1 permanent;
}
root /srv/rails/app.com/current/public;
try_files $uri/index.html $uri @unicorn;
location @unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_unicorn;
}
# Rota para uploads da aplicação
location /arquivos {
# Pra onde o nginx envia a requisição quando o upload terminar
upload_pass @unicorn;
# Local em que o nginx vai subir o arquivo temporario
upload_store /srv/rails/app.com/current/public/uploads/tmp 1;
# Permissão nos arquivos enviados
upload_store_access user:rw group:rw all:r;
# Campos passados no request body para o Rails
upload_set_form_field $upload_field_name[original_filename] "$upload_file_name";
upload_set_form_field $upload_field_name[content_type] "$upload_content_type";
upload_set_form_field $upload_field_name[path] "$upload_tmp_path";
upload_pass_form_field "^X-Progress-ID$|^authenticity_token$";
upload_cleanup 400 404 499 500-505;
track_uploads proxied 30s;
}
location /uploads {
# Força download dos arquivos que estiverem dentro de uploads
if ($request_filename !~* ^.*?\.(jpg)|(png)|(gif)) {
add_header Content-Type "application/octet-stream";
add_header Content-Disposition "attachment; filename=$1";
}
}
location ^~ /progress {
upload_progress_json_output;
report_uploads proxied;
}
client_max_body_size 4G;
keepalive_timeout 10;
error_page 500 502 503 504 /500.html;
location = /500.html {
root /srv/rails/app.com/current/public;
}
}
O arquivo está bem comentado e explicado, tem a configuração do Unicorn e a parte que nos interessa do upload.
A aplicação responde com o progresso do arquivo enviado na url /progress, e eu uso a url /uploads pra forçar o download dos arquivos que foram enviados.
Mas ainda é necessário criar os diretórios temporários que o nginx usará:
# app.com/app/uploaders/arquivo_uploader.rb# encoding: utf-8require'carrierwave/processing/mime_types'classArquivoUploader<CarrierWave::Uploader::BaseincludeCarrierWave::MimeTypesprocess:set_content_typedefmove_to_storetrueend# Choose what kind of storage to use for this uploader:storage:file# Override the directory where uploaded files will be stored.# This is a sensible default for uploaders that are meant to be mounted:defstore_dir"#{Rails.root}/public/uploads"endend
Criamos então uma rota para receber o formulário de arquivos:
E a ação correspondente no Controller para salvar o arquivo enviado:
123456789101112131415161718192021222324252627
# app.com/app/controllers/arquivos_controller.rbdefcreate@arquivo=Arquivo.new(params[:arquivo])# Para receber o arquivo do nginx em produçãoifRails.env!="development"@arquivo.arquivo=ActionDispatch::Http::UploadedFile.new(filename:params['arquivo']['arquivo']['original_filename'],tempfile:File.open(params['arquivo']['arquivo']['path']))endif@arquivo.saverespond_todo|format|format.html{render:json=>@arquivo,:content_type=>'text/html',:layout=>false}format.json{render:json=>@arquivo}endelserender:json=>@arquivo.errors,:status=>:unprocessable_entityendend
A parte mágica aqui é simular um ActionDispatch::Http::UploadedFile sendo passado para o Carrierwave. Dessa forma, a aplicação recebe o arquivo do nginx e o Carrierwave move o arquivo do diretório temporário para o store_dir configurado anteriormente.
Quando o assunto é upload de arquivos a dor de cabeça é quase inevitável. Algumas semanas atrás precisei construir um sistema para upload de arquivos grandes pra um cliente, e durante o caminho aprendi alguns truques úteis.
Esse artigo faz parte de uma série onde mostrarei como instalar e configurar uma aplicação Rails + Carrierwave com nginx + upload module + upload progress em produção com Unicorn.
Por que se importar?
Sem o upload module, a aplicação Rails receberá os dados brutos do upload e analisará todo o conteúdo antes de cuidar do arquivo. Para grandes arquivos isso pode ser bem lento, já que o Ruby estará fazendo todo o trabalho.
Com o upload module, o parsing do arquivo acontece em C através do nginx antes da sua aplicação em Ruby entrar em ação. O módulo coloca o arquivo em um diretório temporário e retira todos parâmetros multipart da requisição POST, os substituindo por parâmetros que você poderá usar na aplicação para pegar o nome e localização do arquivo no disco. Quando a requisição chegar na sua aplicação, todo trabalho pesado já foi concluído e o arquivo está pronto pra ser usado.
O upload progress module informa o progresso do upload, e é um ótimo parceiro do upload module.
Agora compile o nginx com os módulos, note que eu alterei o usuário, grupo e local de instalação do nginx:
1234
$ cd <caminho do source do nginx>
$ ./configure --prefix=/opt/nginx --user=nginx --group=nginx --with-http_ssl_module --add-module=<caminho do upload module> --add-module=<caminho do upload progress module>
$ make
$ sudo make install
Nota: Se você tiver erros por causa de algum módulo, utilize o seguinte comando para o configure:
1
$ CFLAGS=-Wno-unused-but-set-variable ./configure --prefix=/opt/nginx --user=nginx --group=nginx --with-http_ssl_module --add-module=<caminho do upload module> --add-module=<caminho do upload progress module>
Eu gosto de manter o nginx funcionando com a mesma configuração de sites-available e sites-enabled que o Apache usa.
Pra isso, entre no local de instalação do nginx e crie os diretórios:
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
gzip on;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain text/html text/css
application/x-javascript text/xml
application/xml application/xml+rss
text/javascript;
server {
listen 80;
server_name localhost;
# if no listen is specified, all IPv4 interfaces on port 80 are listened to
# to listen on both IPv4 and IPv6 as well, listen [::] and 0.0.0.0 must be specified.
server_name _;
return 444;
}
include sites-enabled/*;
}
Agora só nos resta criar o arquivo de configuração da nossa aplicação e criar o link simbólico para ele:
Esse é um assunto complicado, em vários lugares há publicações sobre REST, RESTful e outros termos derivados. E do outro lado da moeda, existe o bom e velho SOAP. E até recentemente, li um artigo dizendo que um é ‘evolução’ do outro.
Mas, o que é esse tal de REST? De onde ele veio? O que faz? Como funciona?
Na apresentação abaixo, você aprenderá um pouco mais sobre esse assunto e entenderá a relação do REST com o HTTP.
No que você pensa quando ouve falar em Banco de Dados? Não fique surpreso se o termo SQL for um top hit da sua lista, junto com MySQL, PostgreSQL, SQLite, Firebird e vários outros nomes e termos que se associam com o modelo relacional.
Mas outro termo está ficando cada vez mais exposto hoje em dia: NoSQL, uma classe de banco de dados que não possui interface SQL e não se preocupam muito com garantias ACID.
Na apresentação abaixo, você aprenderá onde surgiu o termo, qual o seu papel, os tipos de banco de dados não-relacionais e uma comparação entre um banco de dados relacional e o MongoDB - o banco de dados primário que eu uso em produção.