Exportar archivos a Dropbox en Ruby con el SDK de Dropbox
Integrar las exportaciones de archivos a Dropbox en tus aplicaciones de Ruby puede agilizar tu flujo
de trabajo y ampliar su funcionalidad. Dropbox no publica un SDK oficial para Ruby, así que esta
guía usa la gema dropbox_api, mantenida por la
comunidad, para automatizar las subidas de archivos con ejemplos prácticos.
Requisitos previos
Antes de comenzar, asegúrate de tener:
- Ruby 2.7 o superior (probado hasta Ruby 3.2)
- Bundler 2.0 o superior
- Una cuenta de Dropbox
- Conocimientos básicos de programación en Ruby
- Soporte de SSL en tu entorno de desarrollo
Configurar una app de Dropbox y obtener tokens de acceso
Para interactuar con la API de Dropbox, necesitas crear una app de Dropbox y generar un token de acceso:
-
Crea una app de Dropbox:
- Inicia sesión en la Dropbox App Console.
- Haz clic en Create app.
- Elige Scoped access y selecciona Full Dropbox o App folder, según tus necesidades.
- Dale a tu app un nombre único y haz clic en Create app.
-
Genera un token de acceso:
- En la configuración de tu app, ve a la sección OAuth 2.
- En Generated access token, haz clic en Generate.
- Copia el token de acceso generado; lo usarás en tu aplicación.
- Nota: desde 2021 estos tokens generados en la consola son de corta duración y expiran después de aproximadamente cuatro horas, así que solo sirven mientras experimentas. Para cualquier cosa que ejecutes más de una vez, implementa el flujo completo de OAuth2 y guarda un token de actualización en su lugar.
Instalar el SDK de Dropbox para Ruby
Esta guía usa la gema dropbox_api, mantenida por la comunidad, en su versión 0.1.21, que es la última
versión estable. Agrega la gema a tu Gemfile:
source 'https://rubygems.org'
gem 'dropbox_api', '~> 0.1.21'
Luego instálala con Bundler:
bundle install
Autenticarse con Dropbox en Ruby
DropboxApi::Client.new solo construye un objeto de conexión, por lo que tiene éxito incluso cuando el token es
incorrecto. Haz una única llamada ligera a la API para averiguar si las credenciales funcionan de
verdad:
require 'dropbox_api'
def initialize_dropbox_client(access_token)
client = DropboxApi::Client.new(access_token)
# The constructor never talks to Dropbox; this call is what validates the token.
client.get_current_account
client
rescue DropboxApi::Errors::HttpError
# HttpError#message is built as "HTTP <status>: <body>", so it carries Dropbox's raw response.
# Log the type, and keep the body for a debug sink rather than standard output.
puts 'Could not reach Dropbox: the API returned an unexpected HTTP response'
raise
rescue DropboxApi::Errors::BasicError => e
puts "Dropbox rejected the access token: #{e.class}"
raise
end
# Initialize the client
dbx = initialize_dropbox_client(ENV.fetch('DROPBOX_ACCESS_TOKEN'))
Subir archivos a Dropbox con Ruby
Client#upload hace un POST a /2/files/upload, que recibe el contenido del archivo como una cadena y rechaza
cualquier cosa de más de 150 MB. Como política conservadora, este ejemplo usa una sesión de subida
en ese límite o por encima de él. Client#upload_by_chunks encapsula las sesiones de subida y acepta un manejador
IO abierto:
# /2/files/upload rejects payloads above 150 MB. Dropbox does not say whether it counts decimal
# or binary megabytes, and an upload session accepts any size, so switch over at the lower reading.
MAX_SINGLE_REQUEST_UPLOAD = 150 * 1000 * 1000
def upload_file(client, file_path, dropbox_path)
if File.size(file_path) >= MAX_SINGLE_REQUEST_UPLOAD
File.open(file_path, 'rb') do |file|
client.upload_by_chunks(dropbox_path, file)
end
else
client.upload(dropbox_path, File.binread(file_path))
end
puts "File '#{file_path}' uploaded to '#{dropbox_path}'"
end
# Usage example
begin
local_file = 'path/to/your/local/file.txt'
dropbox_destination = '/Apps/YourAppFolder/file.txt'
upload_file(dbx, local_file, dropbox_destination)
rescue DropboxApi::Errors::BasicError, SystemCallError => e
puts "Failed to upload file: #{e.message}"
rescue DropboxApi::Errors::HttpError => e
# Again: no raw response body on standard output.
puts "Failed to upload file: #{e.class}"
end
Ten en cuenta que la rama de una sola solicitud lee todo el archivo en memoria. Vigila eso si
exportas muchos archivos de forma concurrente, y baja MAX_SINGLE_REQUEST_UPLOAD si tus workers
tienen poca memoria: upload_by_chunks transmite desde el manejador en su lugar.
Manejar errores y excepciones
La limitación de tasa es el único fallo que vale la pena reintentar automáticamente, y la gema ya lo
modela: DropboxApi::Errors::TooManyRequestsError lleva la propia sugerencia retry_after de
Dropbox. Rescata ese tipo en lugar de hacer coincidir el texto del mensaje, y deja que cualquier
otro error llegue al llamador para que una exportación fallida nunca se reporte como un éxito:
def handle_upload_with_retry(client, file_path, dropbox_path, max_retries = 3)
retries = 0
begin
upload_file(client, file_path, dropbox_path)
rescue DropboxApi::Errors::TooManyRequestsError => e
raise if retries >= max_retries
retries += 1
# The gem sets retry_after from `headers['retry-after'].to_i`, so a response without the header
# arrives as 0, and 0 is truthy in Ruby. Test for a positive value, or the backoff never runs.
wait = e.retry_after.to_i.positive? ? e.retry_after.to_i : 2**retries
puts "Rate limited, retrying '#{file_path}' in #{wait}s (attempt #{retries}/#{max_retries})"
sleep(wait)
retry
end
end
TooManyRequestsError desciende de BasicError, así que si agregas una rama rescue DropboxApi::Errors::BasicError, colócala después de esta. De lo contrario, gana el rescate más
amplio y el backoff nunca se ejecuta.
Automatizar las exportaciones de archivos en tu aplicación de Ruby
Automatiza las exportaciones de archivos procesando un directorio completo:
class DropboxExporter
def initialize(access_token)
@client = initialize_dropbox_client(access_token)
end
def process_directory(local_dir, dropbox_dir)
Dir.glob(File.join(local_dir, '*')).each do |file_path|
next unless File.file?(file_path)
dropbox_path = File.join(dropbox_dir, File.basename(file_path))
handle_upload_with_retry(@client, file_path, dropbox_path)
end
end
end
# Usage example
exporter = DropboxExporter.new(ENV.fetch('DROPBOX_ACCESS_TOKEN'))
exporter.process_directory('local/files', '/Apps/YourAppFolder')
process_directory se detiene deliberadamente en el primer archivo que no puede subir, de modo que un
directorio exportado parcialmente aparece como una excepción en lugar de como un éxito silencioso.
Si prefieres exportar todo lo que puedas y reportar el resto, recopila los fallos por archivo y
lanza una sola excepción al final.
Algo que debes prever antes de programar esto: Client#upload usa mode: :add por defecto con
autorename: false, así que una segunda ejecución sobre el mismo directorio lanza un
DropboxApi::Errors::FileConflictError en el primer archivo que ya esté allí. Ese valor por defecto es el
seguro, porque nunca destruye un archivo remoto. Decide de forma explícita qué quieres en su lugar:
- Pasa
autorename: truepara conservar ambas copias, y Dropbox añade un contador al nombre nuevo. - Pasa
mode: :overwritepara reemplazar el archivo remoto. Esto descarta lo que hubiera allí, así que elígelo solo cuando este exportador sea el único que escribe en esa carpeta. - O mantén el valor por defecto y lleva un registro local de los nombres de archivo exportados, para que las nuevas ejecuciones omitan lo que ya llegó.
Consideraciones para la integración con Rails
Si integras las exportaciones de Dropbox dentro de una aplicación de Rails, ten en cuenta los problemas de seguridad de hilos y de bifurcación de procesos. Asegúrate de que cualquier tarea en segundo plano o tarea programada esté configurada con patrones seguros para hilos y bibliotecas de programación robustas, para evitar conflictos de concurrencia, especialmente en entornos de producción.
Conclusión
Integrar el SDK de Dropbox en tu aplicación de Ruby puede automatizar las exportaciones de archivos y agilizar tu flujo de trabajo. Esta guía ofreció un enfoque paso a paso que abarca la autenticación, la subida de archivos, el manejo de errores con lógica de reintentos y el procesamiento de directorios. Para contar con capacidades robustas de manejo y procesamiento de archivos, también podrías explorar los servicios de Transloadit.
