Încărcarea fișierelor cu șine și dragonfly

Cu ceva timp în urmă am scris un articol Încărcarea fișierelor cu șine și altar, care a explicat cum să introduci o caracteristică de încărcare a fișierelor în aplicația Rails cu ajutorul bijuteriei Shrine. Există, totuși, o grămadă de soluții similare disponibile și unul dintre preferatele mele este Dragonfly - o soluție de încărcare ușor de folosit pentru Rails and Rack creată de Mark Evans. 

Am acoperit această bibliotecă la începutul anului trecut, dar, la fel ca în cazul majorității software-urilor, ajută să aruncăm o privire la biblioteci din când în când pentru a vedea ce sa schimbat și cum putem să o folosim în aplicația noastră.

În acest articol vă voi îndruma prin configurarea Dragonfly și explicați cum să utilizați principalele sale caracteristici. Veți învăța cum să:

  • Integrați Dragonfly în aplicația dvs.
  • Configurați modele pentru a lucra cu Dragonfly
  • Introduceți un mecanism de încărcare de bază
  • Introduceți validările
  • Generați miniaturi de imagini
  • Executați procesarea fișierelor
  • Stocați metadatele pentru fișierele încărcate
  • Pregătiți o aplicație pentru implementare

Pentru a face lucrurile mai interesante, vom crea o mică aplicație muzicală. Acesta va prezenta albume și melodii asociate care pot fi gestionate și redate pe site.

Codul sursă pentru acest articol este disponibil la GitHub. Puteți, de asemenea, verifica demo-ul de lucru al aplicației.

Afișarea și gestionarea albumelor

Pentru a începe, creați o nouă aplicație Rails fără suita de testare implicită:

șine noi UploadingWithDragonfly -T

Pentru acest articol voi folosi Rails 5, dar majoritatea conceptelor descrise se aplică și versiunilor mai vechi.

Crearea modelului, a controlerului și a rutelor

Micul nostru site muzical va conține două modele: Album și Cântec. Pentru moment, să creăm primul cu următoarele câmpuri:

  • titlu (şir) - conține titlul albumului
  • cântăreaţă (şir) -album
  • image_uid (şir) - un câmp special pentru stocarea imaginii de previzualizare a albumului. Acest câmp poate fi numit orice doriți, dar trebuie să conțină _uid sufix conform instrucțiunilor din documentația Dragonfly.

Creați și aplicați migrarea corespunzătoare:

rails g model Titlul albumului: string singer: string image_uid: string șir db: migrate

Acum, să creăm un controler generic pentru a gestiona albume cu toate acțiunile implicite:

albums_controller.rb

clasa AlbumsController < ApplicationController def index @albums = Album.all end def show @album = Album.find(params[:id]) end def new @album = Album.new end def create @album = Album.new(album_params) if @album.save flash[:success] = 'Album added!' redirect_to albums_path else render :new end end def edit @album = Album.find(params[:id]) end def update @album = Album.find(params[:id]) if @album.update_attributes(album_params) flash[:success] = 'Album updated!' redirect_to albums_path else render :edit end end def destroy @album = Album.find(params[:id]) @album.destroy flash[:success] = 'Album removed!' redirect_to albums_path end private def album_params params.require(:album).permit(:title, :singer) end end

În cele din urmă, adăugați rutele:

config / routes.rb

resurse: albume

Integrarea Dragonfly

Este timpul ca Dragonfly să intre în lumina reflectoarelor. Mai întâi, adăugați bijuteria în Gemfile:

Gemfile

bijuterie dragonfly

Alerga:

bilele de instalare a șinelor generează dragonfly

Ultima comandă va crea un inițializator numit dragonfly.rb cu configurația implicită. O vom pune deoparte deocamdată, dar puteți citi despre diferitele opțiuni de pe site-ul oficial al Dragonfly.

Următorul lucru important care trebuie făcut este echiparea modelului nostru cu metodele Dragonfly. Acest lucru se face folosind dragonfly_accessor:

modele / album.rb

dragonfly_accessor: imagine

Rețineți că aici vă spun :imagine-se referă în mod direct la image_uid care am creat în secțiunea anterioară. Dacă, de exemplu, ați denumit coloana photo_uid, apoi dragonfly_accessor metoda ar trebui să primească :fotografie ca argument.

Dacă utilizați Rails 4 sau 5, un alt pas important este de a marca :imagine câmp (nu : image_uid!) așa cum este permis în regulator:

albums_controller.rb

params.require (: album) .permite (: titlu,: cântăreață,: imagine)

Acest lucru este destul de mult - suntem gata să creăm vederi și să începem să încărcăm fișierele noastre!

Crearea de vizualizări

Începeți cu vizualizarea index:

vizualizari / albume / index.html.erb

Albume

<%= link_to 'Add', new_album_path %>
    <%= render @albums %>

Acum parțial:

vizualizari / albume / _album.html.erb

  • <%= image_tag(album.image.url, alt: album.title) if album.image_stored? %> <%= link_to album.title, album_path(album) %> de <%= album.singer %> | <%= link_to 'Edit', edit_album_path(album) %> | <%= link_to 'Remove', album_path(album), method: :delete, data: confirm: 'Are you sure?' %>
  • Există două metode Dragonfly care trebuie menționate aici:

    • album.image.url returnează calea spre imagine.
    • album.image_stored? spune dacă înregistrarea are un fișier încărcat în loc.

    Acum adăugați paginile noi și editați:

    vizualizari / albume / new.html.erb

    Adăugați un album

    <%= render 'form' %>

    vizualizari / albume / edit.html.erb

    Editați | × <%= @album.title %>

    <%= render 'form' %>

    vizualizari / albume / _form.html.erb

    <%= form_for @album do |f| %> 
    <%= f.label :title %> <%= f.text_field :title %>
    <%= f.label :singer %> <%= f.text_field :singer %>
    <%= f.label :image %> <%= f.file_field :image %>
    <%= f.submit %> <% end %>

    Forma nu este nimic fantezie, dar, din nou, rețineți că spunem :imagine, nu : image_uid, la redarea intrării fișierului.

    Acum este posibil să bootați serverul și să testați caracteristica de încărcare!

    Eliminarea imaginilor

    Astfel, utilizatorii pot crea și edita albume, însă există o problemă: nu au nici o posibilitate să înlăture o imagine, doar să o înlocuiască cu o altă imagine. Din fericire, acest lucru este foarte ușor de rezolvat prin introducerea unei casete de eliminare a imaginii: 

    vizualizari / albume / _form.html.erb

    <% if @album.image_thumb_stored? %> <%= image_tag(@album.image.url, alt: @album.title) %> <%= f.label :remove_image %> <%= f.check_box :remove_image %> <% end %>

    Dacă albumul are o imagine asociată, îl afișăm și facem o casetă de selectare. Dacă această casetă de selectare este setată, imaginea va fi eliminată. Rețineți că dacă domeniul dvs. este denumit photo_uid, atunci metoda corespunzătoare pentru a elimina atașamentul va fi remove_photo. Simplu, nu-i așa??

    Singurul alt lucru de făcut este permisiunea remove_image atribut în controlorul dvs.:

    albums_controller.rb

    params.require (: album) .permite (: titlu,: cântăreață,: imagine,: remove_image)

    Adăugarea de validări

    În acest stadiu, totul funcționează bine, dar nu verificăm deloc contribuția utilizatorului, ceea ce nu este deosebit de important. Prin urmare, să adăugăm validări pentru modelul albumului:

    modele / album.rb

    validează: titlu, prezență: adevărat validează: cântăreț, prezență: validează adevărat: imagine, prezență: validă validates_property: width, of:: image, in:

    validates_property este metoda Dragonfly care poate verifica diferite aspecte ale atașamentului dvs.: puteți valida extensia unui fișier, tipul MIME, dimensiunea etc..

    Acum, să creăm un parțial generic pentru a face erorile care au fost găsite:

    vizualizari / partajate / _errors.html.erb

    <% if object.errors.any? %> 

    Au fost găsite următoarele erori:

      <% object.errors.full_messages.each do |msg| %>
    • <%= msg %>
    • <% end %>
    <% end %>

    Utilizați acest lucru parțial în interiorul formularului:

    vizualizari / albume / _form.html.erb

    <%= form_for @album do |f| %> <%= render 'shared/errors', object: @album %> <%#… %> <% end %>

    Stilul câmpurilor cu erori un pic pentru a le vizualiza vizual:

    stylesheets / application.scss

    .field_with_errors display: inline; etichetă culoare: roșu;  intrare background-color: lightpink; 

    Păstrarea unei imagini între cereri

    După introducerea validărilor, ne confruntăm cu o altă problemă (destul de scenariu tipic, eh?): Dacă utilizatorul a făcut greșeli în completarea formularului, va trebui să aleagă din nou fișierul după ce a dat clic pe A depune buton.

    Dragonfly vă poate ajuta să rezolvați această problemă și cu ajutorul a retained_ * câmp ascuns:

    vizualizari / albume / _form.html.erb

    <%= f.hidden_field :retained_image %>

    Nu uitați să permiteți și acest domeniu:

    albums_controller.rb

    params.require (: album) .permit (: titlu,: cântăreață,: image,: remove_image,: retained_image)

    Acum imaginea va fi persistenta intre cereri! Singura problemă mică este totuși faptul că intrarea de încărcare a fișierului va afișa în continuare mesajul "alegeți un fișier", dar acest lucru poate fi rezolvat cu ceva stil și o linie de JavaScript.

    Procesarea imaginilor

    Generarea de miniaturi

    Imaginile încărcate de utilizatorii noștri pot avea dimensiuni foarte diferite, care pot (și probabil vor) cauza un impact negativ asupra designului site-ului. Probabil că doriți să scalați imaginile până la anumite dimensiuni fixe și, bineînțeles, acest lucru este posibil prin utilizarea lăţime și înălţime stiluri. Aceasta nu este, însă, o abordare optimă: browserul va trebui să descarce în continuare imagini de dimensiune completă și apoi să le micșoreze.

    O altă opțiune (care este de obicei mult mai bună) este de a genera miniaturi de imagini cu unele dimensiuni predefinite pe server. Acest lucru este foarte simplu de realizat cu Dragonfly:

    vizualizari / albume / _album.html.erb

  • <%= image_tag(album.image.thumb('250x250#').url, alt: album.title) if album.image_stored? %> <%#… %>
  • 250x250 este, desigur, dimensiunile, în timp ce # este geometria care înseamnă "redimensionarea și recoltarea dacă este necesar pentru a menține raportul de aspect cu gravitația centrală". Puteți găsi informații despre alte geometrii pe site-ul web Dragonfly.

    deget mare metoda este alimentată de ImageMagick - o soluție excelentă pentru crearea și manipularea imaginilor. Prin urmare, pentru a vedea demo-ul de lucru la nivel local, va trebui să instalați ImageMagick (toate platformele majore sunt suportate). 

    Suportul pentru ImageMagick este activat implicit în interiorul inițializatorului Dragonfly:

    config / initializatori / dragonfly.rb

    plugin: imaginemagic

    Acum se generează miniaturi, dar acestea nu sunt stocate nicăieri. Aceasta înseamnă că de fiecare dată când un utilizator accesează pagina cu albume, miniaturile vor fi regenerate. Există două modalități de a depăși această problemă: prin generarea acestora după salvarea înregistrării sau prin efectuarea unei generații în zbor.

    Prima opțiune implică introducerea unei coloane noi pentru a stoca miniaturile și pentru a le modifica dragonfly_accessor metodă. Creați și aplicați o nouă migrare:

    rails g migrație add_image_thumb_uid_to_albums image_thumb_uid: șine de șir db: migrează

    Acum modificați modelul:

    modele / album.rb

    dragonfly_accessor: imagine face copy_to (: image_thumb) | a | a.thumb ('250x250 #') sfârșitul dragonfly_accessor: image_thumb

    Rețineți că acum primul apel către dragonfly_accessor trimite un bloc care generează de fapt thumbnail-ul pentru noi și îl copiază în image_thumb. Acum folosiți doar image_thumb metoda în opinia dvs.:

    vizualizari / albume / _album.html.erb

    <%= image_tag(album.image_thumb.url, alt: album.title) if album.image_thumb_stored? %>

    Această soluție este cea mai simplă, dar nu este recomandată de documentele oficiale și, ceea ce este mai rău, la momentul scrisului nu funcționează cu retained_ * câmpuri.

    Prin urmare, permiteți-mi să vă arăt o altă opțiune: generarea miniaturilor în zbor. Aceasta implică crearea unui nou model și ajustarea fișierului de configurare a Dragonfly. În primul rând, modelul:

    rails g model Thumb uid: șir de caractere: string rake db: migrați

    degetele tabelul va găzdui miniaturile dvs., dar acestea vor fi generate la cerere. Pentru a face acest lucru, trebuie să redefinim URL-ul în interiorul inițializatorului Dragonfly:

    config / initializatori / dragonfly.rb

    Dragonfly.app.configure do define_url do | app, job, opts | thumb = Thumb.find_by_job (job.signature) dacă thumb app.datastore.url_for (thumb.uid,: schema => 'https') altceva app.server.url_for (job) end end before_serve do | job, env | uid = job.store Thumb.create! (: uid => uid,: job => job.signature) sfârșitul # ... sfârșit

    Acum adăugați un nou album și vizitați pagina rădăcină. Prima dată când o faceți, următoarea ieșire va fi tipărită în jurnale:

    DRAGONFLY: comanda shell: "converti" "some_path / public / system / dragonfly / development / 2017/02/08 / 3z5p5nvbmx_Folder.jpg" "-prezentarea" "250x250 ^ 250x250 + 0 + 0 "" + repetă "" some_path / 20170208-1692-1xrqzc9.jpg "

    Acest lucru inseamna ca thumbnail-ul este generat pentru noi de catre ImageMagick. Dacă reîncărcați pagina, cu toate acestea, această linie nu va mai apărea, ceea ce înseamnă că miniatură a fost stocată în cache! Puteți citi mai multe despre această funcție pe site-ul Web Dragonfly.

    Mai mult procesare

    Puteți efectua practic orice manipulare a imaginilor după ce au fost încărcate. Acest lucru se poate face în interiorul after_assign suna inapoi. Să transformăm, de exemplu, toate imaginile noastre în format JPEG cu o calitate de 90%: 

    dragonfly_accessor: imagine face după_assign | a | a.encod! ('jpg', '-quality 90') final

    Există multe alte acțiuni pe care le puteți efectua: rotiți și decupați imaginile, codificați-le cu un format diferit, scrieți un text pe acestea, amestecați cu alte imagini (de exemplu, pentru a plasa un filigran) etc. Pentru a vedea alte exemple, consultați secțiunea ImageMagick de pe site-ul Dragonfly.

    Încărcarea și gestionarea cântecelor

    Bineînțeles, partea principală a site-ului nostru muzical este cântece, deci să le adăugăm acum. Fiecare melodie are un titlu și un fișier muzical și aparține unui album:

    rails g model Albume album: belongs_to titlu: string track_uid: string rails db: migrate

    Conectați metodele Dragonfly, așa cum am făcut pentru Album model:

    modele / song.rb

    dragonfly_accessor: piesa

    Nu uitați să stabiliți a are multe relație:

    modele / album.rb

    has_many: melodii, dependente:: distruge

    Adăugați noi rute. O cântec există întotdeauna în sfera unui album, așa că voi face aceste rute imbrăcate:

    config / routes.rb

    resurse: albumele fac resurse: cântece, numai: [: new,: create] end

    Creați un controler foarte simplu (încă o dată, nu uitați să permiteți urmări camp):

    songs_controller.rb

    clasa SongsController < ApplicationController def new @album = Album.find(params[:album_id]) @song = @album.songs.build end def create @album = Album.find(params[:album_id]) @song = @album.songs.build(song_params) if @song.save flash[:success] = "Song added!" redirect_to album_path(@album) else render :new end end private def song_params params.require(:song).permit(:title, :track) end end

    Afișați melodiile și un link pentru a adăuga unul nou:

    vizualizari / albume / show.html.erb

    <%= @album.title %>

    de <%= @album.singer %>

    <%= link_to 'Add song', new_album_song_path(@album) %>
      <%= render @album.songs %>

    Codul formularului:

    vizualizari / cântece / new.html.erb

    Adăugați cântecul la <%= @album.title %>

    <%= form_for [@album, @song] do |f| %>
    <%= f.label :title %> <%= f.text_field :title %>
    <%= f.label :track %> <%= f.file_field :track %>
    <%= f.submit %> <% end %>

    În cele din urmă, adăugați _cântec parțial:

    vizualizari / cântece / _song.html.erb

  • <%= audio_tag song.track.url, controls: true %> <%= song.title %>
  • Aici folosesc HTML5 audio tag, care nu va funcționa pentru browserele mai vechi. Deci, dacă intenționați să sprijiniți astfel de browsere, utilizați un polifil.

    După cum vedeți, întregul proces este foarte simplu. Dragonfly nu are mare grijă ce tip de fișier doriți să încărcați; tot ce trebuie să faceți este să oferiți dragonfly_accessor , adăugați un câmp corespunzător, permiteți-l și faceți o etichetă de introducere a fișierului.

    Stocarea metadatelor

    Când deschid o listă de redare, mă aștept să vadă câteva informații suplimentare despre fiecare melodie, cum ar fi durata sau frecvența de redare. Desigur, în mod implicit aceste informații nu sunt stocate nicăieri, dar putem rezolva acest lucru cu ușurință. Dragonfly ne permite să furnizăm date suplimentare despre fiecare fișier încărcat și să îl preluăm mai târziu utilizând meta metodă.

    Lucrurile sunt însă mult mai complexe atunci când lucrăm cu audio sau video, pentru că pentru a le prelua metadatele, este nevoie de un gem streamio-ffmpeg special. Această bijuterie, la rândul său, se bazează pe FFmpeg, deci pentru a continua va trebui să o instalați pe PC.

    Adăuga streamio-FFMPEG în Gemfile:

    Gemfile

    gem "streamio-ffmpeg"

    Instalați-l:

    instalare pachet

    Acum putem folosi la fel after_assign callback deja văzut în secțiunile anterioare:

    modele / song.rb

    dragonfly_accessor: urmăriți după song = FFMPEG :: Movie.new (a.path) mm, ss = melodie.duration.divmod (60) .map | n | n.to_i.to_s.rjust (2, '0') a.meta ['duration'] = "# mm: # ss" a.meta ['bitrate'] = song.bitrate? song.bitrate / 1000: 0 sfârșitul final

    Rețineți că aici folosesc a cale metoda, nu URL-ul, pentru că în acest moment lucrăm cu un temp template. Apoi extragem durata cântecului (convertirea acestuia în minute și secunde cu zerouri în frunte) și bitrate-ul său (transformându-l în kilobytes pe secundă).

    În cele din urmă, afișați metadatele în vizualizare:

    vizualizari / cântece / _song.html.erb

  • <%= audio_tag song.track.url, controls: true %> <%= song.title %> (<%= song.track.meta['duration'] %>, <%= song.track.meta['bitrate'] %>Kb / s)
  • Dacă verificați conținutul pe publice / sistem / Dragonfly (locația implicită pentru găzduirea încărcărilor), veți nota unele .YML fișiere - stochează toate informațiile meta în format YAML.

    Deplasarea la Heroku

    Ultimul subiect pe care îl vom acoperi astăzi este cum să vă pregătiți aplicația înainte de a vă deplasa la platforma cloud-ului Heroku. Principala problemă este că Heroku nu vă permite să stocați fișiere personalizate (cum ar fi încărcările), așa că trebuie să ne bazăm pe un serviciu de stocare în cloud ca Amazon S3. Din fericire Dragonfly poate fi integrat cu ușurință.

    Tot ce trebuie să faceți este să înregistrați un nou cont la AWS (dacă nu îl aveți deja), să creați un utilizator cu permisiunea de a accesa coșurile S3 și să notați perechea de chei a utilizatorului într-o locație sigură. S-ar putea să utilizați o pereche de cheie rădăcină, dar asta este cu adevărat Nu se recomandă. În cele din urmă, creați o găleată S3.

    Revenind la aplicația Rails, plasați o bijuterie nouă:  

    Gemfile 

    grup: producție gem 'dragonfly-s3_data_store' sfârșit

    Instalați-l:

    instalare pachet

    Apoi configurați Dragonfly pentru a utiliza S3 într-un mediu de producție:

    config / initializatori / dragonfly.rb

    dacă Rails.env.production? datastore: s3, bucket_name: ENV ['S3_BUCKET'], acces_key_id: ENV ['S3_KEY'], secret_access_key: ENV ['S3_SECRET'], regiune: ENV ['S3_REGION'], url_scheme: root_path: Rails.root.join ('public / system / dragonfly', Rails.env), root_root: Rails.root.join ('public') sfârșitul

    A furniza ENV variabile pe Heroku, utilizați această comandă:

    heroku config: adăugați SOME_KEY = SOME_VALUE

    Dacă doriți să testați integrarea cu S3 local, puteți folosi o bijuterie ca dotenv-șine pentru a gestiona variabilele de mediu. Amintiți-vă, totuși, că perechea de chei AWS nu trebuie expuse public!

    O altă problemă mică pe care am întâlnit-o în timpul desfășurării lui Heroku a fost absența FFmpeg. Chestia este că atunci când se creează o nouă aplicație Heroku, ea are un set de servicii care sunt utilizate în mod obișnuit (de exemplu, ImageMagick este disponibil în mod implicit). Alte servicii pot fi instalate ca addons Heroku sau sub forma de buildpacks. Pentru a adăuga un pachet de construcție FFmpeg, executați următoarea comandă:

    pachete de construcție heroku: adăugați https://github.com/HYPERHYPER/heroku-buildpack-ffmpeg.git

    Acum totul este gata, și puteți împărtăși aplicația dvs. muzicală cu lumea!

    Concluzie

    A fost o călătorie lungă, nu-i așa? Astăzi am discutat despre Dragonfly - o soluție pentru încărcarea fișierelor în Rails. Am văzut setarea de bază, unele opțiuni de configurare, generarea de miniaturi, prelucrarea și stocarea metadatelor. De asemenea, am integrat Dragonfly cu serviciul Amazon S3 și am pregătit aplicația noastră pentru implementare pe producție.

    Desigur, nu am discutat despre toate aspectele Dragonfly în acest articol, deci asigurați-vă că navigați pe site-ul său oficial pentru a găsi o documentație extensivă și exemple utile. Dacă aveți alte întrebări sau sunteți blocat (ă) de câteva exemple de cod, nu ezitați să mă contactați.

    Vă mulțumim că ați rămas cu mine și vă voi vedea în curând! 

    Cod