Attachment_fu : améliorer les thumbnails

Bonsoir,

Cette question est assez pointue sur attachment_fu.

Pour un développement en crours, j’utilise attachment_fu pour
sauvegarder en
base de données des images et des documents, avec RMagick comme
processeur
d’images. Ca marche, mais pas tout à fait comme je veux. J’ai besoin
d’ajouter une fonctionnalité permettant de découper une portion de
l’image
et remplacer la vignette correspondante générée par attachment_fu.

Je sais déjà le faire avec RMagick, mais je n’arrive pas à trouver où
dans
attachment_fu ajouter cette fonction (ni d’ailleurs quoi ajouter
précisément).

Bref, si quelqu’un avait ne serait-ce qu’une direction dans laquelle
orienter mes recherches à me conseiller, je serais preneur.

Merci d’avance.


Michel B.

Salut Michel,

Je pense avoir ce qu’il te faut, le mieux me semble un exemple :wink:

Dans ton model en question ajoute la methode suivante :

# Overwrite to remove profiles, support cropping and change

quality
def resize_image(img, size)
# Remove all profiles to save size
img.strip!

  size = size.first if size.is_a?(Array) && size.length == 1 && !

size.first.is_a?(Fixnum)
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?
(Fixnum))
size = [size, size] if size.is_a?(Fixnum)
img.thumbnail!(*size)
else
# Use crop_resized! for cropping purpose
img.change_geometry(size.to_s) { |cols, rows, image|
image.crop_resized!(cols, rows) }
end

  # Drop quality down to 75
  self.temp_path = write_to_temp_file(img.to_blob { self.quality =

75 })
end

Cette fonction remplacera la fonction par default du processeur
RMagick defini par attachment_fu (attachment_fu/lib/technoweenie/
attachment_fu/processors/rmagick.rb)

Dans mon cas je rajoute " img.strip! " pour supprimer tous les
profiles, ca fait gagner environ 20Ko par image et dans mon cas j’ai
pas besoins des profiles.

Ensuite je change " image.resize " par " image.crop_resized! " , cela
supporte donc le recadrage en donnant une taille sous la forme
‘100x100!’, dans le cas present le recadrage ce fait centre sur
l’image

La derniere modif consiste a changer la derniere ligne pour ajouter
" { self.quality = 75 } " ce qui me permet de reduire la qualite de
l’image a 75%, encore une fois un gros gain de poid.

En esperant que ca te sera utile,
Bonne continuation,

Sebastien Grosjean - ZenCocoon

On Sep 27, 12:28 am, “Michel B.” [email protected]

C’est tout à fait le genre de choses qui peut m’aider, merci ^^

Super,

Profite en bien, bon dev :wink:

Sebastien Grosjean - ZenCocoon

On Sep 27, 1:38 am, “Michel B.” [email protected]

Je reviens appeller à l’aide parce que j’ai l’impression d’avoir manqué
un
truc sans savoir lequel.
Toujours dans le contexte de ce même problème, j’ai fait la fonction
suivante dans le modèle d’image :

def recrop_thumb!(top, left, size, thumb)

size = size.first if size.is_a?(Array) && size.length == 1
size = [size.to_i, size.to_i] if ! size.is_a?(Array)

thumb = thumbnails.find(:first, :conditions => {:thumbnail => 

thumb})

thumb.db_file.data = 

Magick::Image.from_blob(db_file.data).first.crop(top,
left, size[0], size[1]).to_blob

thumb.save!

end

Je sais, c’est encore propre, j’essaye avant tout d’avoir quelque chose
qui
fasse à peu près ce que je lui demande, après je verrais pour polir le
fonctionnement.

En gros, après avoir lu les paramètres (top / left pour le coin haut et
gauche du recadrement, size pour les dimensions du recadrement, thumb
pour
le type de la vignette à modifier), on récupère la vignette
correspondante
dans la liste des vignettes de cet objet, puis on met dans “data” le
recadrage, et enfin on sauvegarde la vignette ainsi modifiée.

Apparement rien de transcendental.

Sauf qu’évidement, ça ne marche pas, et ça me casse une erreur
monstrueuse :
NameError: undefined local variable or method full_filename' for #<Image:0xb70d82b8> from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/base.rb:1860:inmethod_missing’
from
./script/…/config/…/config/…/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb:276:in
temp_paths' from ./script/../config/../config/../vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb:270:intemp_path’
from
./script/…/config/…/config/…/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb:236:in
save_attachment?' from ./script/../config/../config/../vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb:339:inset_size_from_temp_path’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/callbacks.rb:333:in
send' from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/callbacks.rb:333:incallback’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/callbacks.rb:330:in
each' from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/callbacks.rb:330:incallback’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/callbacks.rb:295:in
valid?' from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/validations.rb:761:insave_without_transactions!’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/transactions.rb:133:in
save!' from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:59:intransaction’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/transactions.rb:95:in
transaction' from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/transactions.rb:121:intransaction’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/transactions.rb:133:in
save!' from ./script/../config/../config/../app/models/image.rb:70:inrecrop_thumb!’

Je me demande ce que j’ai bien pu faire pour mériter une engueulade
pareille…

Si quelqu’un à une idée, je suis preneur, de toute façon je continue Ã
chercher…

Merci, je vais suivre cette piste ce soir et voir où elle me mène.

On 10/1/07, Michel B. [email protected] wrote:

thumb = thumbnails.find (:first, :conditions => {:thumbnail => thumb})

fasse à peu près ce que je lui demande, après je verrais pour polir le

./script/…/config/…/config/…/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb:270:in
from
`valid?’
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/transactions.rb:95:in

Je me demande ce que j’ai bien pu faire pour mériter une engueulade
pareille…

Si quelqu’un à une idée, je suis preneur, de toute façon je continue à
chercher…

En lisant rapidement le code d’attachment_fu, j’ai l’impression
qu’attachment_fu attend obligatoirement en paramétre de data un
fichier et non un blob. Je pense que si tu crées ton thunbnail
directement dans temp et que tu le passe en param, ca devrait mieux
marché.
En effet il cherche dans le tmp_path à l’endroit où il plante. C’est
tout à fait logique qu’il ne trouve pas de path sachant qu’il n’y en
as pas.

En espérant avoir aidé un petit peu.


Cyril M.

Michel :

Là se posent deux problèmes :
1- attachment_fu appelle resize_image_or_thumbnail! uniquement dans la
méthode process_attachment_with_processing des modules
“processor”, et je n’arrive pas à trouver où est appellé
process_attachment_with_processing, elle n’apparaît nulle
part dans le code de attachment_fu. Apparement elle ne fait pas plus partie
de ActiveRecord::Base, du coup je suis dans le noir complet.

Je réponds déjà à ça :

dans chaque processeur, tu trouves le callback included qui
sera appelé lorsque le module est mixé dans ta classe.

Dans cette méthode, il y a cette ligne :

base.alias_method_chain :process_attachment, :processing

qui va te créer deux alias :
process_attachment_without_processing avec l’ancienne méthode
process_attachment

et process_attachment vers la méthode process_attachment_with_processing
définie dans le module.

Donc tu vas “décorer” la méthode process_attachment initialement
définie dans la classe de base.

– Jean-François.


Ruby ( http://www.rubyfrance.org ) on Rails ( http://www.railsfrance.org
)

Toujours en train de batailler avec attachment_fu…

Suite au bug d’hier, j’ai tenté une autre approche plus proche de ce que
fait attachment_fu :

def recrop_image(img, top, left, size)
size = size.first if size.is_a?(Array) && size.length == 1
size = [size.to_i, size.to_i] if ! size.is_a?(Array)
img.crop(top, left, size[0], size[1])
self.temp_path = write_to_temp_file(img.to_blob { self.quality = 75
})
end

def recrop_image_or_thumbnail!(img, top, left)
if (!respond_to?(:parent_id) || parent_id.nil?) &&
attachment_options[:resize_to] # parent image
resize_image(img, top, left, attachment_options[:resize_to])
elsif thumbnail_resize_options # thumbnail
resize_image(img, top, left, thumbnail_resize_options)
end
end

De nouveau rien de transcendental, je reprend dans l’idée le code des
fonctions resize_image et resize_image_or_thumbnail! (qui semble la
seule Ã
appeller resize_image dans le code de attachment_fu). J’ajoute donc
toujours
deux paramètres top et left toujours pour permettre de définir le bord
haut
et gauche du recadrement de l’image, et j’effectue un recadrement à la
place
du redimensionnement.

Là se posent deux problèmes :
1- attachment_fu appelle resize_image_or_thumbnail! uniquement dans la
méthode process_attachment_with_processing des modules “processor”, et
je
n’arrive pas à trouver où est appellé
process_attachment_with_processing,
elle n’apparaît nulle part dans le code de attachment_fu. Apparement
elle ne
fait pas plus partie de ActiveRecord::Base, du coup je suis dans le noir
complet.
2- j’ai essayé de déclencher “à la main” ma méthode
recrop_image_or_thumbnail!, puis de sauver la vignette, et ça ne mène
qu’Ã
une erreur :

image = Image.find(:first, :conditions => {:thumbnail => nil}, :order =>
‘id desc’)
=> …

thumb = image.thumbnails.find(:first, :conditions => {:thumbnail =>
‘thumb’})
=> …

thumb.recrop_image_or_thumbnail!(Magick::Image.from_blob(
image.db_file.data).first, 0, 0)
=> nil

thumb.save!
NameError: undefined local variable or method full_filename' for #<Image:0xb71636c4> from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/base.rb:1860:inmethod_missing’
from
./script/…/config/…/config/…/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb:276:in
temp_paths' from ./script/../config/../config/../vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb:270:intemp_path’
from
./script/…/config/…/config/…/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb:236:in
save_attachment?' from ./script/../config/../config/../vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb:339:inset_size_from_temp_path’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/callbacks.rb:333:in
send' from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/callbacks.rb:333:incallback’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/callbacks.rb:330:in
each' from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/callbacks.rb:330:incallback’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/callbacks.rb:295:in
valid?' from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/validations.rb:761:insave_without_transactions!’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/transactions.rb:133:in
save!' from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:59:intransaction’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/transactions.rb:95:in
transaction' from ./script/../config/../config/../vendor/rails/activerecord/lib/active_record/transactions.rb:121:intransaction’
from
./script/…/config/…/config/…/vendor/rails/activerecord/lib/active_record/transactions.rb:133:in
`save!’
from (irb):4

Bref, je crois que je loupe un truc vital d’attachment_fu sans réussir Ã
mettre le doigt dessus. Je me demande si ça ne vient pas du fait
qu’attachment_fu travaille normalement à partir d’une image stockée dans
le
système de fichier, fait les recadrage en pilote automatique une bonne
fois
pour toute et basta, l’image est stockée et c’est censé être stockée une
fois pour toute on nettoie et on y touche plus. En gros, ce que j’essaye
de
faire en copiant l’existant ne pourrait marcher qu’en remplacement de
l’existant, ce que je ne souhaite pas pour toutes ces raisons :

  • je veux garder attachment_fu aussi fonctionnel qu’au premier jour
  • le comportement par défaut (redimensionner + découper) me va très bien
  • mon action nécessite deux paramètres supplémentaires qui vont varier
    pour
    chaque image, les stocker dans la définition du modèle ne serait pas une
    bonne idée
  • la nouvelle action est censée se produire après l’enregistrement de
    l’image lorsque l’utilisateur souhaitera remplacer la vignette
    redimensionnée + recadrée par une vignette simplement recadrée à un
    endroit
    stratégique de l’image ; autant avoir entre temps de vignettes
    fonctionnelles enregistrées

Voilà , sur la fin c’était moins une question qu’une réflexion écrite,
mais
bon si ça inspire quelqu’un, je suis à cours d’idées pour ce matin de
toute
façon.

Merci encore d’avoir tenu jusqu’Ã la fin de ce long mail.

Michel :

Merci encore d’avoir tenu jusqu’à la fin de ce long mail.

Bon je scratche tout, parce que hein. On a pleins de détails mais
il manque quand même le point de départ, c’est-à-dire comment
tu utilises has_attachment, faut le déviner d’après le code
et le backtrace, c’est un peu pénible.

Sauf erreur, le thumbnail est la même classe que l’image elle-même
et le backend est la base de données.

Ma question est : que se passe-t-il si tu choisis comme backend
le système de fichiers.

Je me le demande car #full_filename est défini dans les backend fs et s3,
mais pas db.

C’est tout pour le moment.

– Jean-François.


Ruby ( http://www.rubyfrance.org ) on Rails ( http://www.railsfrance.org
)

(désolé pour avoir tardé à donner ces précisions élémentaires, j’ai eu
une
grosse journée)

Voici donc la façon dont j’ai utilisé attachment_fu à l’origine :

has_attachment :content_type => :image,
:storage => :db_file,
:processor => “Rmagick”,
:thumbnails => { :small => “170x>”,
:thumb => [50,50],
:micro => [20,20] }
validates_as_attachment

Je n’ai pas encore eu le temps de tester avec l’attachement dans le
système
de fichier, mais quoi qu’il en soit il faut qu’au final ça marche en
conservant les données dans la base de données, pas dans le système de
fichier ni chez amazon.

Bon, j’ai essayé en mettant :storage => :file_system

Avec dans image une image fraîchement uploadée, je fais en console :

thumb = image.thumbnails.find(:first, :conditions => {:thumbnail =>
“thumb”})
=> …

thumb.recrop_image_or_thumbnail!(image, 10, 10)
=> nil

Mais quand j’accède à mon image, rien n’a changé.

J’essaye ensuite :

thumb.save!
=> true

Mais toujours pas de résultat.

Puis :

image.save!
=> true

Toujours rien.

Michel :

C’est agaçant de se trouver bloqué comme ça tout près du but.
C’est sûr, je pourrais m’en moquer et mettre les valeurs en dûr ou
les passer en paramètres, mais ce serait nettement moins élégant
puisque je veux de toute façon conserver les mêmes dimensions
que celles définies dans has_attachment.

attachment_options[:thumbnails][img.thumbnail.to_sym]

je ne garantis rien !

– Jean-François.


Ruby ( http://www.rubyfrance.org ) on Rails ( http://www.railsfrance.org
)

Ah, je crois que je touche au but.

Avec le code suivant :

mgk_img = Magick::Image.from_blob(image.db_file.data).first.crop!(10, 10,
50, 50)
=> JPEG 200x250=>50x50 200x250+10+10 DirectClass 8-bit 24kb

thumb.db_file.data = mgk_img.to_blob
=> …

thumb.db_file.save!
=> true

J’ai obtenu ce que je voulais en console (l’onglet :thumb de l’image
s’est
bien sauvegardé recadré comme souhaité dans la base de données).

Du coup j’ai adapté mes méthodes dans l’object Image :

def recrop_image_or_thumbnail!(img, top, left)
if (!respond_to?(:parent_id) || parent_id.nil?) &&
attachment_options[:resize_to] # parent image
recrop_image!(img, top, left, attachment_options[:resize_to])
elsif thumbnail_resize_options # thumbnail
recrop_image!(img, top, left, thumbnail_resize_options)
end
end

def recrop_image!(img, top, left, size)
size = size.first if size.is_a?(Array) && size.length == 1
size = [size.to_i, size.to_i] if ! size.is_a?(Array)
mgk_img = Magick::Image.from_blob(img.db_file.data).first.crop!(top,
left, size[0], size[1])
self.db_file.data = mgk_img.to_blob
self.db_file.save!
end

Et ça marche… rait, si thumbnail_resize_options n’était pas vide dans
la
ligne :

elsif thumbnail_resize_options # thumbnail

Je ne vois aucune raison pour qu’il soit vide, après tout il est censé
être
rempli lorsque je fais has_attachment avec le tableau [50,50]. Quand je
le
test dans la console, j’obtiens :

image.thumbnail_resize_options
=> nil

C’est agaçant de se trouver bloqué comme ça tout près du but. C’est sûr,
je
pourrais m’en moquer et mettre les valeurs en dûr ou les passer en
paramètres, mais ce serait nettement moins élégant puisque je veux de
toute
façon conserver les mêmes dimensions que celles définies dans
has_attachment
.

Si quelqu’un à encore une idée, merci d’avance…

Merci, je vais essayer de suite ^^



Et ça marche en plus !!

Merci à tous de m’avoir aidé à débloquer cette solution, je la toilette
un
peu et je la met à disposition asap.