2012-03-28 17 views
19

Mam mały Rails 3.2.1 aplikacja, która wykorzystuje CarrierWave 0.5.8 do wysyłania plików do S3 (za pomocą FOG)Pobieranie plików skompresowanie, które zostały dodane do S3 z CarrierWave

chcę, aby użytkownicy mogli wybierać niektóre zdjęcia, które chcieliby pobrać, a następnie zapakuj je i wyślij im suwak. Oto co mam wymyślić:

def generate_zip 
    #A collection of Photo objects. The Photo object has a PhotoUploader mounted. 
    photos = Photo.all 

    tmp_filename = "#{Rails.root}/tmp/" << Time.now.strftime('%Y-%m-%d-%H%M%S-%N').to_s << ".zip" 
    zip = Zip::ZipFile.open(tmp_filename, Zip::ZipFile::CREATE) 
    zip.close 

    photos.each do |photo| 
    file_to_add = photo.photo.file 
    zip = Zip::ZipFile.open(tmp_filename) 
    zip.add("tmp/", file_to_add.path) 
    zip.close 
    end 

    #do the rest.. like send zip or upload file and e-mail link 

end 

To nie działa, ponieważ photo.photo.file zwraca instancję CarrierWave :: STORAGE :: Fog :: pliku zamiast zwykłego pliku.

EDIT: Błąd ten prowadzi do:

ERRNO :: ENOENT: Nie ma takiego pliku lub katalogu - uploads/zdjęcia/name.jpg

Próbowałem również:

tmp_filename = "#{Rails.root}/tmp/" << Time.now.strftime('%Y-%m-%d-%H%M%S-%N').to_s << ".zip" 
    zip = Zip::ZipFile.open(tmp_filename, Zip::ZipFile::CREATE) 
    zip.close 

    photos.each do |photo| 
     processed_uri = URI.parse(URI.escape(URI.unescape(photo.photo.file.authenticated_url)).gsub("[", "%5B").gsub("]", "%5D")) 
     file_to_add = CarrierWave::Uploader::Download::RemoteFile.new(processed_uri) 
     zip = Zip::ZipFile.open(tmp_filename) 
     zip.add("tmp/", file_to_add.path) 
     zip.close 
    end 

Ale to daje mi 403. Pewna pomoc byłaby bardzo doceniana ... To chyba nie jest takie trudne Ja po prostu robię to Wrong ™

+0

Z jakiego klejnotu korzystałeś? Klejnot Rubizip? –

Odpowiedz

16

udało mi się rozwiązać problem z pomocą @ffoeg

Rozwiązanie oferowane przez @ffoeg nie działa całkiem tak dobrze dla mnie, ponieważ miałem do czynienia z plikami zip> 500 MB, które spowodowały problemy na Heroku. Mam do nich przeniósł skompresowanie do procesu w tle przy użyciu resque:

app/pracowników/photo_zipper.rb:

require 'zip/zip' 
require 'zip/zipfilesystem' 
require 'open-uri' 
class PhotoZipper 
    @queue = :photozip_queue 

    #I pass 
    def self.perform(id_of_object_with_images, id_of_user_to_be_notified) 
    user_mail = User.where(:id => id_of_user_to_be_notified).pluck(:email) 
    export = PhotoZipper.generate_zip(id_of_object_with_images, id_of_user_to_be_notified) 

    Notifications.zip_ready(export.archive_url, user_mail).deliver 
    end 

    # Zipfile generator 
    def self.generate_zip(id_of_object_with_images, id_of_user_to_be_notified) 
    object = ObjectWithImages.find(id_of_object_with_images) 
    photos = object.images 
    # base temp dir 
    temp_dir = Dir.mktmpdir 
    # path for zip we are about to create, I find that ruby zip needs to write to a real file 
    # This assumes the ObjectWithImages object has an attribute title which is a string. 
    zip_path = File.join(temp_dir, "#{object.title}_#{Date.today.to_s}.zip") 

    Zip::ZipOutputStream.open(zip_path) do |zos| 
     photos.each do |photo| 
     path = photo.photo.path 
     zos.put_next_entry(path) 
     zos.write photo.photo.file.read 
     end 
    end 

    #Find the user that made the request 
    user = User.find(id_of_user_to_be_notified) 

    #Create an export object associated to the user 
    export = user.exports.build 

    #Associate the created zip to the export 
    export.archive = File.open(zip_path) 

    #Upload the archive 
    export.save! 

    #return the export object 
    export 
    ensure 

    # clean up the tempdir now! 
    FileUtils.rm_rf temp_dir if temp_dir 
    end 


end 

app/controllers/photos_controller.rb:

format.zip do 
    #pick the last ObjectWithImages.. ofcourse you should include your own logic here 
    id_of_object_with_images = ObjectWithImages.last.id 

    #enqueue the Photozipper task 
    Resque.enqueue(PhotoZipper, id_of_object_with_images, current_user.id) 

    #don't keep the user waiting and flash a message with information about what's happening behind the scenes 
    redirect_to some_path, :notice => "Your zip is being created, you will receive an e-mail once this process is complete" 
    end 

Wielkie dzięki @ffoeg za pomoc mi. Jeśli twoje zamki są mniejsze, możesz wypróbować rozwiązanie @ ffoeg.

+0

Witam, czy archiwum jest kolumną tabeli db o nazwie export? Jaki jest typ danych? dwójkowy? Dzięki – user1883793

10

Oto moje ujęcie. Nie może być literówki, ale myślę, że to jest sedno tego :)

# action method, stream the zip 
def download_photos_as_zip # silly name but you get the idea 
    generate_zip do |zipname, zip_path| 
    File.open(zip_path, 'rb') do |zf| 
     # you may need to set these to get the file to stream (if you care about that) 
     # self.last_modified 
     # self.etag 
     # self.response.headers['Content-Length'] 
     self.response.headers['Content-Type'] = "application/zip" 
     self.response.headers['Content-Disposition'] = "attachment; filename=#{zipname}" 
     self.response.body = Enumerator.new do |out| # Enumerator is ruby 1.9 
     while !zf.eof? do 
      out << zf.read(4096) 
     end 
     end 
    end 
    end 
end 


# Zipfile generator 
def generate_zip(&block) 
    photos = Photo.all 
    # base temp dir 
    temp_dir = Dir.mktempdir 
    # path for zip we are about to create, I find that ruby zip needs to write to a real file 
    zip_path = File.join(temp_dir, 'export.zip') 
    Zip::ZipFile::open(zip_path, true) do |zipfile| 
    photos.each do |photo| 
     zipfile.get_output_stream(photo.photo.identifier) do |io| 
     io.write photo.photo.file.read 
     end 
    end 
    end 
    # yield the zipfile to the action 
    block.call 'export.zip', zip_path 
ensure 
    # clean up the tempdir now! 
    FileUtils.rm_rf temp_dir if temp_dir 
end 
+0

Dziękuję bardzo za odpowiedź. Używam Ruby 1.9.3-p125 i pojawia się błąd (po skorygowaniu niektórych literówek), który zipfile.open wywołuje metodę prywatną. (prywatna metoda "otwarta" wymagająca # ) – Gidogeek

+0

hmm, sprawdzę inny projekt, w którym to zrobiłem. – ffoeg

+0

czy możesz mi pokazać linię w twoim Gemfile, gdzie przyniesiesz klejnot rubyzip? – ffoeg

Powiązane problemy