Blog

Download private file trên S3 về browser trong rails

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 thêm:

KHOÁ HỌC TRUY VẤN VÀ THAO TÁC DỮ LIỆU SQL TỪ CƠ BẢN ĐẾN NÂNG CAO

KHÓA HỌC DATA WAREHOUSE : TỔNG HỢP, CHUẨN HÓA VÀ XÂY DỰNG KHO DỮ LIỆU TRONG DOANH NGHIỆP

KHÓA HỌC DATA MODEL – THIẾT KẾ MÔ HÌNH DỮ LIỆU TRONG DOANH NGHIỆP

LỘ TRÌNH TRỞ THÀNH DATA ENGINEER CHO NGƯỜI MỚI BẮT ĐẦU

DATA ENGINEER LÀ GÌ? CÔNG VIỆC CHÍNH CỦA DATA ENGINEER? CÁC KỸ NĂNG CẦN THIẾT

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

Leave a Reply

Your email address will not be published. Required fields are marked *