Nếu file trên S3 được public thì chỉ cần trả về url của file cho user, khi user click vào url đó trên browser thì họ sẽ download được.
Còn nếu file trên S3 là private và ta muốn download file thông qua API thì làm như sau:
- Download S3 file rồi lưu về file tạm(temporary file) trên server.
- Gửi nội dung file tạm về cho người dùng
- Xóa file tạm trên server(có thể xóa hoặc không nhưng nên xóa đi)
Khi không cần xóa file tạm thì bạn chỉ cần làm như sau (ở đây mình dùng gem aws-sdk-resources, nếu bạn dùng gem khác thì gọi với API tương ứng):
def download
s3 = Aws::S3::Resource.new(region: ENV['AWS_REGION'])
obj = s3.bucket(ENV['BUCKET_NAME']).object("file_path_in_S3")
tempFile = Tempfile.new('test')
obj.get(response_target: tempFile.path)
send_file(tempFile.read, filename: "file_name")
end
Khi gọi send_file
Rails server sẽ gửi request tới HTTP server(Apache hoặc Nginx), HTTP server khi nhận được request send_file sẽ gửi file cho client. Vì thế, nếu ta xóa temp file ngay sau send_file sẽ phát sinh lỗi do Rails không biết được là HTTP server có đang sử dụng file tạm đó hay không.
Đoạn code sau sẽ trả về file không đúng cho client:
def download
s3 = Aws::S3::Resource.new(region: ENV['AWS_REGION'])
obj = s3.bucket(ENV['BUCKET_NAME']).object("file_path_in_S3")
tempFile = Tempfile.new('test')
obj.get(response_target: tempFile.path)
send_file(tempFile.read, filename: "file_name")
tempFile.unlink # wrong here
end
Để xóa được file tạm thì thay vì dùng send_file
ta dùng send_data
như sau:
def download
s3 = Aws::S3::Resource.new(region: ENV['AWS_REGION'])
obj = s3.bucket(ENV['BUCKET_NAME']).object("file_path_in_S3")
tempFile = Tempfile.new('test')
obj.get(response_target: tempFile.path)
File.open(tempFile.path, 'r') do |f|
send_data(f.read, filename: "file_name")
end
ensure
if tempFile.present?
tempFile.close
tempFile.unlink
end
end
Link tham khảo: https://stackoverflow.com/questions/5535981/what-is-the-difference-between-send-data-and-send-file-in-ruby-on-rails