För en tid sedan skrev jag en artikel Ladda upp filer med skenor och helgedom som förklarade hur man introducerar en filuppladdningsfunktion i din Rails-applikation med hjälp av Shrine-pärlan. Det finns dock en massa liknande lösningar, och en av mina favoriter är Dragonfly-en lättanvänd uppladdningslösning för Rails and Rack skapad av Mark Evans.
Vi täckte det här biblioteket i början av förra året men som med de flesta programvaror hjälper det att titta på bibliotek från tid till annan för att se vad som ändrats och hur vi kan använda det i vår ansökan.
I den här artikeln kommer jag att vägleda dig genom installationen av Dragonfly och förklara hur du använder de viktigaste funktionerna. Du kommer att lära dig hur:
För att göra sakerna mer intressanta, kommer vi att skapa en liten musikalisk applikation. Den kommer att presentera album och tillhörande låtar som kan hanteras och spelas upp på webbplatsen.
Källkoden för den här artikeln finns på GitHub. Du kan också kolla in den fungerande demo av ansökan.
För att starta, skapa en ny Rails-applikation utan standard testpaket:
skenar nya UploadingWithDragonfly -T
För den här artikeln använder jag Rails 5, men de flesta av de beskrivna begreppen gäller även äldre versioner.
Vår lilla musikaliska webbplats kommer att innehålla två modeller: Album
och Låt
. För nu, låt oss skapa den första med följande fält:
titel
(sträng
) -Innehåller albumets titelsångare
(sträng
) -albumets artisterimage_uid
(sträng
)-ett specialfält för att lagra albumets förhandsgranskningsbild. Det här fältet kan namnges allt du vill, men det måste innehålla _uid
suffix enligt instruktioner från Dragonfly-dokumentationen.Skapa och tillämpa motsvarande migrering:
skenor g modell Album titel: strängsångare: sträng image_uid: strängskenor db: migrera
Låt oss nu skapa en mycket generisk kontroller för att hantera album med alla standardåtgärder:
klassalbumkontrollen < 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
Slutligen lägg till rutterna:
resurser: album
Det är dags för Dragonfly att gå in i rampljuset. Lägg först pärlan i Gemfile:
pärla "slända"
Springa:
buntinstallationsskenor genererar slända
Det senare kommandot skapar en initierare som heter dragonfly.rb med standardkonfigurationen. Vi lägger det åt sidan för tillfället, men du kan läsa om olika alternativ på Dragonflys officiella hemsida.
Nästa viktiga sak är att utrusta vår modell med Dragonflys metoder. Detta görs genom att använda dragonfly_accessor
:
dragonfly_accessor: bild
Observera att jag säger här :bild
-det hänför sig direkt till image_uid
kolumn som vi skapade i föregående avsnitt. Om du till exempel namngav din kolumn photo_uid
, sedan dragonfly_accessor
metod skulle behöva ta emot :Foto
som ett argument.
Om du använder Rails 4 eller 5, är ett annat viktigt steg att markera :bild
fält (inte : image_uid
!) som tillåtet i regulatorn:
params.require (: album) .permit (: title,: sångare,: bild)
Det här är ganska mycket - vi är redo att skapa synpunkter och börja ladda upp våra filer!
Börja med indexvyn:
album
<%= link_to 'Add', new_album_path %>
Nu är det partiella:
Det finns två Dragonfly-metoder att notera här:
album.image.url
returnerar sökvägen till bilden.album.image_stored?
säger om posten har en uppladdad fil på plats.Lägg nu till de nya och redigera sidorna:
Lägg till album
<%= render 'form' %>
Redigera <%= @album.title %>
<%= render 'form' %>
<%= 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 %>
Formen är inget fancy, men återigen notera att vi säger :bild
, inte : image_uid
, när filens inmatning görs.
Nu kan du starta servern och testa uppladdningsfunktionen!
Så användarna kan skapa och redigera album, men det finns ett problem: de har ingen möjlighet att ta bort en bild, bara för att ersätta den med en annan. Lyckligtvis är det här mycket enkelt att fixa genom att införa en kryssruta för "ta bort bild":
<% if @album.image_thumb_stored? %> <%= image_tag(@album.image.url, alt: @album.title) %> <%= f.label :remove_image %> <%= f.check_box :remove_image %> <% end %>
Om albumet har en tillhörande bild visar vi den och gör en kryssruta. Om den här kryssrutan är inställd tas bilden bort. Observera att om ditt fält heter photo_uid
, då kommer motsvarande metod att ta bort bifogad fil remove_photo
. Enkelt, är det inte?
Den enda andra sak att göra är att tillåta remove_image
attribut i din controller:
params.require (: album) .permit (: title,: sångare,: bild,: remove_image)
I detta skede fungerar allt bra, men vi kontrollerar inte användarens ingång alls, vilket inte är särskilt bra. Låt oss därför lägga till valideringar för albummodellen:
validerar: titel, närvaro: true validates: sångare, närvaro: true validates: bild, närvaro: true validates_property: width, of:: image, in: (0 ... 900)
validates_property
är Dragonfly-metoden som kan kontrollera olika aspekter av din bilaga: du kan validera en filens filtillägg, MIME-typ, storlek etc.
Låt oss nu skapa en generisk partiell för att göra de fel som hittades:
<% if object.errors.any? %><% end %>Följande fel hittades:
<% object.errors.full_messages.each do |msg| %>
- <%= msg %>
<% end %>
Anlita detta partiellt inuti formuläret:
<%= form_for @album do |f| %> <%= render 'shared/errors', object: @album %> <%#… %> <% end %>
Stil fälten med fel lite för att visuellt skildra dem:
.field_with_errors display: inline; etikett färg: röd; inmatning bakgrundsfärg: lightpink;
Vi har infört valideringar, vi löser ännu ett problem (ganska typiskt scenario, eh?): Om användaren har gjort fel när han fyller i formuläret måste han eller hon välja filen igen efter att ha klickat på Lämna knapp.
Dragonfly kan hjälpa dig att lösa detta problem också med hjälp av en behöll_*
dolt fält:
<%= f.hidden_field :retained_image %>
Glöm inte att tillåta detta fält också:
params.require (: album) .permit (: title,: sångare,: bild,: remove_image,: retained_image)
Nu kommer bilden att fortsätta mellan förfrågningar! Det enda lilla problemet är emellertid att filuppladdningseffekten fortfarande kommer att visa meddelandet "välj ett fil", men det kan fixas med viss styling och ett streck av JavaScript.
Bilderna uppladdade av våra användare kan ha mycket olika dimensioner, vilket kan (och förmodligen) få negativ inverkan på webbplatsens design. Du skulle förmodligen vilja skala bilder ner till vissa fasta dimensioner, och det är naturligtvis möjligt genom att använda bredd
och höjd
stilar. Detta är dock inte ett optimalt tillvägagångssätt: webbläsaren behöver fortfarande ladda ner fullstora bilder och krympa dem sedan.
Ett annat alternativ (som vanligtvis är mycket bättre) är att generera bildminnebilder med några fördefinierade dimensioner på servern. Det här är väldigt enkelt att uppnå med Dragonfly:
250x250
är givetvis dimensionerna, medan #
är geometrin som betyder "ändra storlek och beskära om det behövs för att behålla bildförhållandet med mittens gravitation". Du kan hitta information om andra geometrier på Dragonflys hemsida.
De tumme
Metoden drivs av ImageMagick-en bra lösning för att skapa och manipulera bilder. För att du ska kunna se den fungerande demo lokalt måste du installera ImageMagick (alla större plattformar stöds).
Stöd för ImageMagick är aktiverat som standard inuti Dragonfly's initializer:
plugin: imagemagick
Nu skapas miniatyrer, men de lagras inte någonstans. Det innebär att varje gång en användare besöker albumsidan kommer miniatyrerna att regenereras. Det finns två sätt att övervinna detta problem: genom att generera dem efter att rekordet sparats eller genom att utföra generation i flyg.
Det första alternativet innebär att du inför en ny kolumn för att lagra miniatyren och justera dragonfly_accessor
metod. Skapa och tillämpa en ny migrering:
rails g migration add_image_thumb_uid_to_albums image_thumb_uid: strängskenor db: migrera
Ändra nu modellen:
dragonfly_accessor: bilden gör copy_to (: image_thumb) | a | a.thumb ('250x250 #') avsluta dragonfly_accessor: image_thumb
Observera att nu det första samtalet till dragonfly_accessor
skickar ett block som faktiskt genererar miniatyren för oss och kopierar den till image_thumb
. Använd nu bara image_thumb
metod i dina åsikter:
<%= image_tag(album.image_thumb.url, alt: album.title) if album.image_thumb_stored? %>
Denna lösning är den enklaste, men det rekommenderas inte av de officiella dokumenten och vad som är värre vid skrivningstid fungerar det inte med behöll_*
fält.
Låt mig därför visa dig ett annat alternativ: generera miniatyrbilder i fluga. Det innebär att skapa en ny modell och tweaking Dragonfly's config-fil. Först, modellen:
skenor g modell Thumb uid: string job: string rake db: migrera
De tummen
bordet kommer att vara värd för dina miniatyrbilder, men de kommer att genereras på begäran. För att detta ska ske måste vi omdefiniera url
metod inuti Dragonfly-initieraren:
Dragonfly.app.configure gör define_url göra | app, jobb, opts | thumb = Thumb.find_by_job (job.signature) om thumb app.datastore.url_for (thumb.uid,: scheme => 'https') annars app.server.url_for (jobb) slutändan before_serve do | job, env | uid = job.store Thumb.create! (: uid => uid,: job => job.signature) slutet # ... slutet
Lägg nu till ett nytt album och besök rotsidan. Första gången du gör det kommer följande utskrift att skrivas ut i loggarna:
DRAGONFLY: shell command: "convert" "some_path / public / system / dragonfly / development / 2017/02/08 / 3z5p5nvbmx_Folder.jpg" "-resize" "250x250 ^^" "-gravity" "Center" "-crop" 250x250 + 0 + 0 "" + repage "" some_path / 20170208-1692-1xrqzc9.jpg "
Det innebär att miniatyrbilden genereras för oss av ImageMagick. Om du laddar om sidan, kommer den här raden inte att visas längre, vilket innebär att miniatyren har cachat! Du kan läsa lite mer om den här funktionen på Dragonflys hemsida.
Du kan utföra praktiskt taget någon manipulering av dina bilder efter att de har laddats upp. Detta kan göras inuti after_assign
ring tillbaka. Låt oss till exempel konvertera alla våra bilder till JPEG-format med 90% kvalitet:
dragonfly_accessor: bilden gör after_assign | a | a.encode! ('jpg', '-quality 90') slutet
Det finns många fler åtgärder du kan utföra: rotera och beskär bilderna, koda med ett annat format, skriv text på dem, mixa med andra bilder (till exempel placera ett vattenstämpel), etc. För att se några andra exempel, se ImageMagick-avsnittet på Dragonfly-webbplatsen.
Naturligtvis är huvuddelen av vår musikaliska webbplats låtar, så låt oss lägga till dem nu. Varje låt har en titel och en musikalisk fil, och den hör till ett album:
skenor g modell Sångalbum: belong_to title: string track_uid: strängskenor db: migrera
Haka upp Dragonfly-metoderna, som vi gjorde för Album
modell:
dragonfly_accessor: track
Glöm inte att skapa en har många
relation:
has_many: låtar, beroende:: förstör
Lägg till nya rutter. En sång finns alltid i ett albums räckvidd, så jag ska göra dessa rutter nestade:
resurser: album gör resurser: låtar, bara: [: ny,: skapa] slut
Skapa en mycket enkel kontroller (återigen, glöm inte att tillåta Spår
fält):
klass 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
Visa låtarna och en länk för att lägga till en ny:
<%= @album.title %>
av <%= @album.singer %>
<%= link_to 'Add song', new_album_song_path(@album) %><%= render @album.songs %>
Koda formuläret:
Lägg till låt till <%= @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 %>
Slutligen lägg till _låt partiell:
Här använder jag HTML5 audio
tagg som inte fungerar för äldre webbläsare. Så, om du syftar till att stödja sådana webbläsare, använd en polyfil.
Som du ser är hela processen väldigt enkel. Dragonfly bryr sig inte riktigt vilken typ av fil du vill ladda upp. allt du behöver göra är att ge en dragonfly_accessor
metod, lägg till ett korrekt fält, tillåt det och gör en filinmatningstagg.
När jag öppnar en spellista förväntar jag mig att se ytterligare information om varje låt, som dess längd eller bitrate. Naturligtvis är denna information som standard inte lagrad någonstans, men vi kan ordna det ganska enkelt. Dragonfly tillåter oss att ge ytterligare data om varje uppladdad fil och hämta den senare med hjälp av meta
metod.
Saker är dock lite mer komplexa när vi arbetar med ljud eller video, för att hämta deras metadata krävs en speciell pärla streamio-ffmpeg. Denna pärla är i sin tur beroende av FFmpeg, så för att kunna fortsätta måste du installera den på din dator.
Lägg till Streamio-ffmpeg
in i Gemfile:
pärla 'streamio-ffmpeg'
Installera det:
buntinstallation
Nu kan vi använda samma after_assign
återuppringning redan sett i föregående avsnitt:
dragonfly_accessor: spår gör after_assign do | a | låt = FFMPEG :: Movie.new (a.path) mm, ss = song.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 slutet slutet
Observera att här använder jag en väg
metod, inte url
, för att vi just nu arbetar med en tempfile. Nästa extraherar vi bara låtens varaktighet (konvertera den till minuter och sekunder med ledande nollor) och dess bitrate (konvertera den till kilobyte per sekund).
Slutligen, visa metadata i vyn:
Om du kontrollerar innehållet på publika / system / trollslända mapp (standardplatsen som värd för uppladdningarna), noterar du några .YML filer - de lagrar all metainformation i YAML-format.
Det sista ämnet vi kommer att täcka idag är hur du förbereder din ansökan innan du distribuerar till Heroku Cloud Platform. Det största problemet är att Heroku inte tillåter dig att lagra anpassade filer (som uppladdningar), så vi måste förlita oss på en cloud storage-tjänst som Amazon S3. Lyckligtvis kan Dragonfly enkelt integreras med det.
Allt du behöver göra är att registrera ett nytt konto hos AWS (om du inte redan har det), skapa en användare med tillstånd att komma åt S3-hinkar och skriv ner användarens nyckelpar på en säker plats. Du kan använda ett rotnyckelpar, men det här är verkligen rekommenderas inte. Slutligen skapa en S3 hink.
Gå tillbaka till vår Rails-applikation, släpp in en ny pärla:
grupp: tillverkning gör perle "dragonfly-s3_data_store" slutet
Installera det:
buntinstallation
Därefter tweak Dragonflys konfiguration för att använda S3 i en produktionsmiljö:
om Rails.env.production? datastore: s3, bucket_name: ENV ['S3_BUCKET'], access_key_id: ENV ['S3_KEY'], secret_access_key: ENV ['S3_SECRET'], region: ENV ['S3_REGION'], url_scheme: 'https' annars datastore: root_path: Rails.root.join ("public / system / dragonfly", Rails.env), server_root: Rails.root.join ("public") slutet
Att förse ENV
variabler på Heroku, använd det här kommandot:
heroku config: lägg till SOME_KEY = SOME_VALUE
Om du vill testa integration med S3 lokalt kan du använda en pärla som dotenv-skenor för att hantera miljövariabler. Kom ihåg dock att ditt AWS-nyckelpar får inte vara offentligt utsatta!
Ett annat litet problem som jag har stött på när jag genomförde till Heroku var frånvaron av FFmpeg. Saken är att när en ny Heroku-applikation skapas, har den en uppsättning tjänster som vanligtvis används (till exempel är ImageMagick tillgängligt som standard). Övriga tjänster kan installeras som Heroku addons eller i form av buildpacks. För att lägga till en FFmpeg buildpack, kör följande kommando:
heroku buildpacks: lägg till https://github.com/HYPERHYPER/heroku-buildpack-ffmpeg.git
Nu är allt klart, och du kan dela din musikaliska applikation med världen!
Det här var en lång resa, eller hur? Idag har vi diskuterat Dragonfly-en lösning för filuppladdning i Rails. Vi har sett sin grundläggande inställning, vissa konfigurationsalternativ, miniatyrbildsgenerering, bearbetning och lagring av metadata. Vi har också integrerat Dragonfly med Amazon S3-tjänsten och förberett vår applikation för distribuering vid produktion.
Naturligtvis har vi inte diskuterat alla aspekter av Dragonfly i den här artikeln, så se till att bläddra i sin officiella hemsida för att hitta omfattande dokumentation och användbara exempel. Om du har några andra frågor eller är fast med några kodexempel, tveka inte att kontakta mig.
Tack för att du bodde hos mig och vi ses snart!