Rails Upload Handler
Problem
You need to process uploaded archive files in a Rails controller, extracting and processing their contents.
Solution
Use Excavate with proper error handling and temporary directory management:
# app/services/archive_processor.rb
require 'excavate'
class ArchiveProcessor
def initialize(uploaded_file)
@uploaded_file = uploaded_file
end
def process
target = Dir.mktmpdir('archive-')
begin
archive = Excavate::Archive.new(@uploaded_file)
archive.extract(target, recursive_packages: true)
results = process_extracted_files(target)
{ success: true, results: results }
rescue Excavate::TargetExistsError => e
{ success: false, error: 'Target already exists', message: e.message }
rescue Excavate::TargetNotEmptyError => e
{ success: false, error: 'Target not empty', message: e.message }
rescue Excavate::UnknownArchiveError => e
{ success: false, error: 'Unknown archive format', message: e.message }
rescue Excavate::Error => e
{ success: false, error: 'Extraction failed', message: e.message }
ensure
FileUtils.rm_rf(target)
end
end
private
def process_extracted_files(directory)
files = Dir.glob("#{directory}/**/*").select { |f| File.file?(f) }
files.map do |file|
{
path: file.sub("#{directory}/", ''),
size: File.size(file),
type: File.extname(file)
}
end
end
endController Example
# app/controllers/archives_controller.rb
class ArchivesController < ApplicationController
def new
end
def create
unless valid_archive?(params[:file])
redirect_to new_archive_path, alert: 'Invalid archive format'
return
end
processor = ArchiveProcessor.new(params[:file])
result = processor.process
if result[:success]
redirect_to archives_path,
notice: "Processed #{result[:results].size} files"
else
redirect_to new_archive_path, alert: result[:message]
end
end
private
def valid_archive?(file)
return false unless file.respond_to?(:original_filename)
ext = File.extname(file.original_filename).downcase
%w[.zip .tar .tar.gz .tgz .tar.xz .txz .msi .rpm .pkg .7z].any? do |valid|
ext.end_with?(valid)
end
end
endWith File Storage
# app/services/archive_importer.rb
require 'excavate'
class ArchiveImporter
def initialize(uploaded_file, user)
@uploaded_file = uploaded_file
@user = user
end
def import
target = Dir.mktmpdir('import-')
imported_files = []
begin
archive = Excavate::Archive.new(@uploaded_file)
archive.extract(target, recursive_packages: true)
# Import files to ActiveStorage
Dir.glob("#{target}/**/*").each do |file|
next unless File.file?(file)
relative_path = file.sub("#{target}/", '')
imported_file = @user.imported_files.create!(
filename: File.basename(file),
path: relative_path,
size: File.size(file),
file: File.open(file)
)
imported_files << imported_file
end
{ success: true, files: imported_files }
rescue StandardError => e
{ success: false, error: e.message }
ensure
FileUtils.rm_rf(target)
end
end
endBackground Job
# app/jobs/archive_processing_job.rb
class ArchiveProcessingJob < ApplicationJob
queue_as :default
def perform(uploaded_file_path, user_id)
user = User.find(user_id)
target = Dir.mktmpdir('archive-job-')
begin
archive = Excavate::Archive.new(uploaded_file_path)
archive.extract(target, recursive_packages: true)
process_files(target, user)
ensure
FileUtils.rm_rf(target)
File.delete(uploaded_file_path) if File.exist?(uploaded_file_path)
end
end
private
def process_files(directory, user)
Dir.glob("#{directory}/**/*").each do |file|
next unless File.file?(file)
# Process each file
ImportFileService.new(file, user).call
end
end
end