Wrzucanie kilku/wielu zdjęć w jednym formularzu

Potrzebuję formularza dla modelu, gdzie mógłbym wrzucać dodatkowo kilka/wiele zdjęć naraz, przypisanych do tego modelu na zasadzie Model has_many photos.
Czy jest coś sensownego do tego zadania oprócz attachment_fu z udziwnieniami? “Z udziwnieniami” mam na myśli z hackami potrzebnymi do tego, żeby przyjmował więcej niż jeden attachment, bo standardowo tego nie obsługuje.

Nie spotkałem się z takim rozwiązaniem, ale gdzie leży problem w wykorzystaniu attachment_fu? Robisz klasy powiedzmy:

class Article has_many photos end class Photo attachemnt_fu ... end
W widoku robisz sobie pole, w którym określasz ilość zdjęć i dalej JS-em lub AJAX-em generujesz sobie odpowiednie pola na pliki.
W kontrolerze tworzysz najpierw wiele zdjeć, a potem wiążesz to z obiektem klasy Article.

Jeśli przyjąć Twoje rozwiązanie (tzn. wiele zdjęć do jednego obiektu), to będzie problem z zarządzaniem nimi - np. dodanie lub usunięcie pojedynczego zdjęcia będzie musiało być napisane od zera (zamiast wykorzystać metody AR). Chyba, że zawsze wiesz, że jeden obiekt ma dokładnie określoną liczbę zdjęć, ale wtedy moje rozwiązanie też nie jest szczególnie problematyczne.

[quote=apohllo]Nie spotkałem się z takim rozwiązaniem, ale gdzie leży problem w wykorzystaniu attachment_fu? Robisz klasy powiedzmy:

class Article has_many photos end class Photo attachemnt_fu ... end
W widoku robisz sobie pole, w którym określasz ilość zdjęć i dalej JS-em lub AJAX-em generujesz sobie odpowiednie pola na pliki.
W kontrolerze tworzysz najpierw wiele zdjeć, a potem wiążesz to z obiektem klasy Article.[/quote]
Zamiast w kontrolerze tworzyć zdjęcia lepiej odpowiedzialność za to przerzucić na model Article:

[code=ruby]class Article
has_many :photos

after_update :save_photos

def photos_attributes=(photos_attributes)
photos_attributes.each do |attributes|
photos.build(attributes)
end
end

protected
def save_photos
photos.each do |p|
p.save
end
end
end

class Photo

has_attachment …
end[/code]
Polecam obejrzeć wszystkie 3 odcinki railscastów pt. Complex Forms

Ok, chyba dokładnie tak zrobię. Dzięki za pomoc.
Complex forms kiedyś zgłębiałem, ale dawno tego nie robiłem, więc mi się zapomniało.
Swoją drogą Railscasts potwierdza po raz już nie wiadomo który swoją klasę.

Fakt - sam to tak zaimplementowałem w swojej aplikacji, a teraz plotę bzdury :wink:

W razie czego u siebie na blogusiu pisałem o ułatwieniu dodawania zdjęć przy użyciu jQuery: http://drogomir.com/blog/2008/7/3/tweaking-rails-app-with-jquery-part-i (właściwie to można dokładnie to samo zrobić z prototype, ale to już musiałbyś sam dziergać).

Oprócz tego gdzieś na forum wrzucałem kiedyś kod do obsługiwania w aplikacji zipów.

Ooooooo… kod do obsługiwania zip’ów to by mi się przydał. Spróbuję poszukać.
Dzięki za info.

Tak spojrzałem teraz na ten kod i to było do file_column. Niby nie taka duża różnica, ale komu się chce męczyć? :wink:

Łap kod do attachment_fu:

[code] def self.get_from_zip(uploaded_file, user, ip)
files = []
documents = []

path = "/tmp/#{uploaded_file.original_filename}"
File.open(path, "w") do |file|
  file.write(uploaded_file.read)
end
Zip::ZipInputStream::open(path) do |zipfile|

  while zip_entry = zipfile.get_next_entry
    files << zip_entry.to_s unless zip_entry.directory?
  end
end

Zip::ZipFile.open(path) do |zipfile|
  files.each do |f|
    if zipfile.file.file?(f)
      filename = zipfile.file.basename(f)
      content_type = File.mime_type?(zipfile.file.basename(f))
      options = {:content_type => content_type, :title => "#{filename}",:filename => filename, :user => user, :user_ip => ip}
      document = Document.new(options)
      document.temp_data = zipfile.file.read(f)
      documents << document unless document.save
    end
  end
end
documents

end

def self.handle_upload(document_params, user, ip)
documents = []
if document_params[:uploaded_data].kind_of? Array
docs = document_params[:uploaded_data]
else
docs = [document_params]
end

docs.to_a.each do |p|
  unless p.blank?
    if p.original_filename =~ /\.zip$/i
      documents += self.get_from_zip(p, user, ip)
    else
      options = {:uploaded_data => p, :title => "#{p.original_filename}", :user => user, :user_ip => ip}
      document = Document.new(options)
      document.content_type = File.mime_type?(p.original_filename)
      documents << document unless document.save
    end
  end
end

documents

end[/code]
To jest kod do zrefaktoryzowania i wrzucenia w plugin od dłuższego czasu, ale cholera czasu brak… jak zwykle.

Mam nadzieję, że się przyda :slight_smile:

drogus,

Jak się wpatruję w ten kod, to nie do końca go rozumiem.
Skoro self.handle_upload() zwraca obiekt documents, to jak zrozumieć tę linijkę:

documents << document unless document.save

Nigdzie nie widziałem tam document.save, więc się pogubiłem…

Akurat tutaj chodziło mi o to, żeby zwrócić tylko te obiekty, które nie mogły zostać zapisane.

Przykład:
Użytkownik uploaduje 10 plików - 5 z nich jest ok, są zapisane, 5 ma jakieś błędy przy walidacji. Metodę handle_upload można wtedy wywołać w kotrolerze i jeżeli zwrócona tablica nie jest pusta wyświetlić błędy tylko dla konkretnych plików.

Mam pytanie odnośnie późniejszego redirect_to po multi uploadzie, otóż jak przekombinować article_path(@photo.article) jeżeli użyjemy mulit uploadu i używamy permalinka, więc nie można użyć params(:article_id) zamias @photo.article?

Zupełnie nie czaję o co chodzi w tym pytaniu. :slight_smile:

@drogus przeczytałem twój wpis:
http://drogomir.com/blog/2008/7/3/tweaking-rails-app-with-jquery-part-i

i mam pytanie jak się ma takie rozwiązanie wydajnościowo do rozwiązania “zwykłego” czyli mam formularz, 4 pola na pliki, daje upload, strona się przeładowuje… Mam na myśli np 100 userów robiących to w tym samym czasie?

W jakim sensie czym się różni moje rozwiązanie?

Plików się nie da przesłać ajaxem, więc i tak request trafia do iframe’a, więc tu nie ma żadnej różnicy.

Chodzi o ilość plików?

Wysyłam twoim rozwiązaniem jeden plik i wysyłam jeden plik “normalnie”. Czy będzie jakakolwiek różnica w wydajności (progress bar,odczytanie stanu postępu, dodatkowe biblioteki itp)?

No tak, jakaś różnica pewnie będzie, ale zdziwiłbym się jeżeli będzie większa niż błąd pomiaru.

Podpowiedź:
Odczytanie postępu - po stronie użytkownika.
Odczytanie i zapisanie stanu uploadu na serwerze - apache, C, zapisanie/odczytanie kilku zmiennych typu int i wygenerowanie kilkudziesięciu bajtów odpowiedzi.
Obsłużenie tego po stronie rails - dokładnie takie samo jak normalnie, z tym że dostajesz tablicę zamiast pojedyńczego pliku.

Pomijam tutaj wrzucenie zipa, bo to chyba jasne, że czas będzie dłuższy o rozpakowanie owego zipa. Dalej praktycznie to samo.