Exporting files to Supabase Buckets in Ruby

Supabase offers a robust cloud storage solution through its bucket system. Although the official Ruby SDK for Supabase is under active development and does not yet support storage operations, you can interact with Supabase Storage using Ruby's built-in HTTP libraries. In this guide, we illustrate how to export files to Supabase storage buckets via the REST API.
Prerequisites
Before beginning, ensure you have:
- Ruby 2.7 or higher installed
- A Supabase account and project with storage enabled
- Basic familiarity with Ruby programming
- Your Supabase project URL and API key
Setting up your environment
Create a new Ruby project and set up the necessary dependencies. Although some libraries like
net/http
and json
come with Ruby, we include them for clarity, and add mime-types
for
determining file content types:
mkdir supabase-storage-demo
cd supabase-storage-demo
bundle init
Add the following gems to your Gemfile:
source 'https://rubygems.org'
# Net/http and JSON are part of Ruby's standard library
gem 'mime-types'
Install the dependencies:
bundle install
Configuring a REST API client in Ruby
Since direct storage operations via a Ruby SDK are not available, we'll build a simple client using Ruby's Net::HTTP to interact with the Supabase Storage REST API.
require 'net/http'
require 'json'
require 'mime/types'
class SupabaseStorage
def initialize(project_url, api_key)
@project_url = project_url.chomp('/')
@api_key = api_key
@storage_url = "#{@project_url}/storage/v1"
end
private
def headers
{
'Authorization' => "Bearer #{@api_key}",
'apikey' => @api_key
}
end
def make_request(uri, request)
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(request)
end
end
public
# Upload a file to a specific bucket and destination path
def upload_file(bucket_name, file_path, destination_path)
uri = URI("#{@storage_url}/object/#{bucket_name}/#{destination_path}")
request = Net::HTTP::Post.new(uri)
headers.each { |key, value| request[key] = value }
# Open the file in binary mode and set up the request stream
File.open(file_path, 'rb') do |file|
request.body_stream = file
request['Content-Type'] = MIME::Types.type_for(file_path).first.to_s
request['Content-Length'] = File.size(file_path).to_s
response = make_request(uri, request)
JSON.parse(response.body)
end
rescue StandardError => e
{ 'error' => e.message }
end
# List files in a bucket with an optional prefix path
def list_files(bucket_name, path = '')
uri = URI("#{@storage_url}/object/list/#{bucket_name}")
uri.query = URI.encode_www_form(prefix: path)
request = Net::HTTP::Get.new(uri)
headers.each { |key, value| request[key] = value }
response = make_request(uri, request)
JSON.parse(response.body)
rescue StandardError => e
{ 'error' => e.message }
end
# Delete a file from a bucket; returns true if deletion is successful
def delete_file(bucket_name, file_path)
uri = URI("#{@storage_url}/object/#{bucket_name}/#{file_path}")
request = Net::HTTP::Delete.new(uri)
headers.each { |key, value| request[key] = value }
response = make_request(uri, request)
response.code == '200' || response.code == '204'
rescue StandardError => e
false
end
end
Usage examples
Below is an example of how to use the SupabaseStorage
class to upload, list, and delete files from
a Supabase bucket.
# Initialize the client with your environment variables
storage = SupabaseStorage.new(
ENV['SUPABASE_URL'],
ENV['SUPABASE_ANON_KEY']
)
# Upload a file
result = storage.upload_file(
'my-bucket',
'path/to/local/image.jpg',
'uploads/image.jpg'
)
if result['error']
puts "Upload failed: #{result['error']}"
else
puts 'File uploaded successfully'
end
# List files in a bucket
files = storage.list_files('my-bucket', 'uploads/')
if files.is_a?(Array)
files.each do |file|
puts "File: #{file['name']}, Size: #{file.dig('metadata', 'size')}"
end
else
puts "Error listing files: #{files['error']}"
end
# Delete a file
if storage.delete_file('my-bucket', 'uploads/image.jpg')
puts 'File deleted successfully'
else
puts 'Failed to delete file'
end
Error handling and file validation
It is important to validate files before uploading to avoid errors and ensure security. The following validator checks for file existence, size limits, and allowed file types.
class FileValidator
MAX_FILE_SIZE = 50 * 1024 * 1024 # 50MB
ALLOWED_TYPES = %w[.jpg .jpeg .png .pdf .doc .docx]
def self.validate!(file_path)
raise 'File not found' unless File.exist?(file_path)
size = File.size(file_path)
raise 'File exceeds maximum size' if size > MAX_FILE_SIZE
extension = File.extname(file_path).downcase
raise 'Invalid file type' unless ALLOWED_TYPES.include?(extension)
true
end
end
# Example usage:
begin
FileValidator.validate!('path/to/file.jpg')
storage.upload_file('my-bucket', 'path/to/file.jpg', 'uploads/file.jpg')
rescue StandardError => e
puts "Validation failed: #{e.message}"
end
Best practices
Generate unique filenames
Creating unique filenames helps prevent collisions in storage. For example:
require 'securerandom'
def generate_unique_filename(original_filename)
extension = File.extname(original_filename)
basename = File.basename(original_filename, extension)
timestamp = Time.now.strftime('%Y%m%d-%H%M%S')
"#{basename}-#{timestamp}-#{SecureRandom.hex(4)}#{extension}"
end
Organize files by date
Storing files in date-based directories can simplify management:
def generate_storage_path(filename)
date = Time.now
"uploads/#{date.year}/#{date.month}/#{filename}"
end
Handling large files
Supabase Storage's REST API does not currently support native chunked uploads. For large file uploads, you might need to implement custom logic by splitting your file into smaller chunks and uploading them sequentially or using an intermediary server solution.
def upload_large_file(bucket_name, file_path, destination_path, chunk_size = 5_242_880)
File.open(file_path, 'rb') do |file|
while (chunk = file.read(chunk_size))
# Implement your custom chunked upload logic here.
# This may involve storing chunks temporarily and then finalizing the upload.
puts "Uploading chunk of size #{chunk.size} bytes..."
# For demonstration, we simply note the chunk size.
end
end
end
Common use cases
- User avatar uploads
- Document storage systems
- Media file management
- Backup solutions
- Content delivery systems
Security considerations
- Validate files before uploading to prevent issues.
- Store your Supabase project URL and API key in environment variables.
- Implement proper CORS policies if exposing your API.
- Set appropriate bucket permissions.
- Use secure URLs for sensitive content.
Conclusion
While the official Ruby SDK for Supabase remains under active development, using Ruby's Net::HTTP and the Supabase REST API provides a viable method for managing file storage. With proper validation, error handling, and security measures, you can efficiently export files to Supabase storage buckets in your Ruby applications.
For additional media processing needs, consider exploring Transloadit's features to further enhance your workflow.