2017-01-18 19 views
0

我正在使用回形針和AWS S3進行文件存儲。鐵路大衆下載

我有一個汽車模型和一個圖像模型。 汽車has_many :images。 圖片has_attachment :file

一輛汽車可以擁有儘可能多的圖像。

我想要的是一種同時下載所有這些汽車圖像的方法。

我有工作代碼:

def download 
    @images = @car.images 

    compressed_filestream = Zip::OutputStream.write_buffer do |zos| 
    @images.each do |img| 
     zos.put_next_entry img.file_file_name 
     zos.print open(img.file.url).read 
    end 
    end 

    compressed_filestream.rewind 
    send_data compressed_filestream.read, filename: "#{@car.name}.zip" 
end 

當請求/cars/1/download上述控制器操作運行。 它的作品,但我覺得它很慢。 我現在想要的是一個更快的解決方案,用於批量下載。 我發現每兆字節下載時間需要6秒。

我想要更快的方法。 我知道你可以去任何網頁,右鍵單擊和「另存爲...」以保存該特定頁面。 當頁面有圖像時,它們會在下載完成後出現在新文件夾中。 下載速度也非常快。 我想這是因爲瀏覽器已經下載了這些圖像,所以它只是讓他們到我的電腦,而不是再次獲取圖像。 如果瀏覽器可以下載HTML文件和資產文件夾,我們應該能夠讓瀏覽器下載一個圖像文件夾的權利?

我有幾個想法,我會努力,但我想知道是否有人有一些更快的解決方案或至少輸入當前的想法。

思路:

  1. 相反起草新的.zip文件每次有人想下載的,編輯的.zip文件,每次汽車的圖像得到更新。這種方式當用戶請求所有圖像時,文件已經存在,並且他們只是下載它。但是這些.zip文件應該放在哪裏?我們在哪裏以及如何拯救他們?

  2. 在JavaScript中,您可以使用一些圖像url創建blob文件。我們可以加載頁面加載後的所有圖像嗎?這樣頁面加載速度很快,但在後臺,當用戶查看頁面時,瀏覽器正在後臺下載圖像。如果用戶決定下載它們,則下載時間很快。

  3. 也許我的控制器操作可以改進,以更快地創建臨時.zip文件。

想法任何人?

+1

假設網絡帶寬不是瓶頸,你最好在壓縮時並行下載和互斥。 Typhoeus使這個簡單而有效,fwiw。可能還值得*不*壓縮太多/根本。 JPG數據不會壓縮太多。 [這是另一個有趣的選項。](http://stackoverflow.com/a/21210576/203130) – coreyward

+0

@coreyward我得到它的工作,並從40秒下載6MB到1.7秒。我不知道你的壓縮互斥量是什麼意思。我也沒有使用Typhoeus。我對這些數字感到滿意,壓縮和Typhoeus上的互斥量會進一步提高嗎? –

回答

0

下面是選項3的答案。在原始代碼中,.each方法等待每個循環完成後再繼續下一個循環。如果圖片從互聯網下載到服務器的平均時間爲1秒,則需要下載約40秒的40個圖片下載。相反,請同時下載所有文件。爲此,請使用Threads

class CarsController < ApplicationController 
    def download 
    images = load_images 

    filestream = write_file images 

    send_data filestream.read, filename: "#{@car.name}.zip" 
    end 

    def load_images 
    threads = [] 
    images = [] 

    @car.images.each do |f| 
     threads << Thread.new do 
     images << { name: f.file_file_name, file: open(f.file.url).read } 
     end 
    end 

    threads.each(&:join) 

    images 
    end 

    def write_file(images) 
    require 'zip' 

    Zip.default_compression = Zlib::NO_COMPRESSION 

    stream = Zip::OutputStream.write_buffer do |zos| 
     images.each do |img| 
     zos.put_next_entry img[:name] 
     zos.print img[:file] 
     end 
    end 

    stream.rewind 

    stream 
    end 
end 

您使用線程將圖像及其名稱鏟成陣列。將該數組信息傳遞給zipfile編寫器方法。一旦寫入zipfile,將其發送給用戶。

這將控制器操作運行時間從40秒減少到1..2秒。

0

你最好在這裏使用原始緩存系統與aws s3寶石一起使用。

首先,您將在S3中創建一個名爲car_image_zips的存儲桶。當有人點擊下載時,你會接觸到這個存儲桶,看看是否存在汽車圖像壓縮文件。如果有,請下載。如果沒有,請下載所有文件並創建zip並上傳。這裏需要注意的一點是,如果您的實現使用Sidekiq等後臺作業,您可以通過將後續上傳作爲後臺作業進行優化。

所以說: 我會認爲@car有一個id。這也假定您已正確配置AWS S3 gem。所以下載會是這個樣子:

def download 
car_id = @car.id 
s3 = AWS::S3.new #should be added as constant somewhere 
bucket = s3.buckets['car_image_zips'] 
if buckets.object["#{car_id}_zip"].exists? #Sample naming scheme 
    send_data s3.get_object(bucket:'car_image_zips', key:"#{car_id}_zip").body.read 
else 
    # Zip up files like you have 
    @images = @car.images 
    compressed_filestream = Zip::OutputStream.write_buffer do |zos| 
    @images.each do |img| 
     zos.put_next_entry img.file_file_name 
     zos.print open(img.file.url).read 
    end 
    end 
    compressed_filestream.rewindsend_data 
    s3_obj = s3.bucket('car_image_zips').object("#{@car.name}.zip") 
    s3_obj.upload_file("#{@car.name}.zip") 
    send_data compressed_filestream.read, filename: "#{@car.name}.zip" 
end 

當然,我沒有測試過這一點,但是這應該給你如何使用基本的緩存做一個大致的瞭解。這並不完美,因爲您需要下載並處理一次,但這對於相對簡單的解決方案來說是一個巨大的收益。

如果您真的想要優化,您可以使用類似AWS Lambda函數的功能在每次將文件上載到S3並使其可供下載時創建zip。