For en tid siden skrev jeg en artikkel Last opp filer med skinner og helligdom som forklarte hvordan du introduserer en filopplastingsfunksjon i din Rails-applikasjon ved hjelp av Shrine-perlen. Det finnes imidlertid en rekke liknende løsninger, og en av mine favoritter er Dragonfly-en brukervennlig opplastingsløsning for Rails and Rack opprettet av Mark Evans.
Vi dekket dette biblioteket tidlig i fjor, men som med de fleste programvare hjelper det å se på biblioteker fra tid til annen for å se hva som er endret og hvordan vi kan bruke det i vår søknad.
I denne artikkelen vil jeg veilede deg gjennom oppsettet av Dragonfly og forklare hvordan du bruker de viktigste funksjonene. Du vil lære hvordan:
For å gjøre ting mer interessant, skal vi lage en liten musikalsk applikasjon. Det vil presentere album og tilhørende sanger som kan styres og spilles på nettsiden.
Kildekoden for denne artikkelen er tilgjengelig på GitHub. Du kan også sjekke ut den aktive demoen i søknaden.
For å starte, opprett et nytt Rails-program uten standard testing suite:
skinner nye UploadingWithDragonfly -T
For denne artikkelen vil jeg bruke Rails 5, men de fleste av de beskrevne konseptene gjelder også for eldre versjoner.
Vårt lille musikalske nettsted skal inneholde to modeller: Album
og Sang
. For nå, la oss lage den første med følgende felt:
tittel
(string
)-inneholder albumets tittelsanger
(string
) -albumets utøverimage_uid
(string
) - Et spesielt felt for å lagre albumets forhåndsvisningsbilde. Dette feltet kan bli navngitt alt du liker, men det må inneholde _uid
suffiks som beskrevet av Dragonfly-dokumentasjonen.Opprett og bruk tilsvarende migrering:
skinner g modell Album tittel: streng sanger: streng image_uid: strengskinner db: migrere
La oss nå lage en veldig generell kontroller for å administrere album med alle standardhandlinger:
klassen 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
Til slutt legger du til ruttene:
ressurser: album
Det er på tide for Dragonfly å gå inn i rampelyset. Først legger du perlen til Gemfile:
perle 'dragonfly'
Løpe:
bunt installasjonsskinner genererer dragonfly
Sistnevnte kommando vil opprette en initialiserer som heter dragonfly.rb med standardkonfigurasjonen. Vi vil legge den til side for nå, men du kan lese om forskjellige alternativer på Dragonflys offisielle nettside.
Den neste viktige tingen å gjøre er å utruste vår modell med Dragonfly-metoder. Dette gjøres ved å bruke dragonfly_accessor
:
dragonfly_accessor: image
Merk at her sier jeg :bilde
-det relaterer seg direkte til image_uid
kolonne som vi opprettet i forrige seksjon. Hvis du for eksempel oppgav kolonnen din photo_uid
, og så dragonfly_accessor
metoden vil trenge å motta : bilde
som et argument.
Hvis du bruker Rails 4 eller 5, er et annet viktig trinn å markere :bilde
felt (ikke : image_uid
!) som tillatt i kontrolleren:
params.require (: album) .permit (: title,: sanger,: bilde)
Dette er ganske mye det - vi er klare til å lage visninger og begynne å laste opp våre filer!
Start med indeksvisningen:
album
<%= link_to 'Add', new_album_path %>
Nå er den delvise:
Det er to Dragonfly-metoder å merke seg her:
album.image.url
returnerer banen til bildet.album.image_stored?
sier om posten har en opplastet fil på plass.Legg nå til de nye og redigere sidene:
Legg til album
<%= render 'form' %>
Redigere <%= @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 %>
Skjemaet er ikke noe fancy, men igjen merke til at vi sier :bilde
, ikke : image_uid
, når gjengivelse av filinngangen.
Nå kan du starte opp serveren og teste opplastingsfunksjonen!
Så brukerne kan lage og redigere album, men det er et problem: de har ingen måte å fjerne et bilde, bare for å erstatte det med en annen. Heldigvis er dette veldig enkelt å fikse ved å introdusere en "fjern bilde" -boks:
<% if @album.image_thumb_stored? %> <%= image_tag(@album.image.url, alt: @album.title) %> <%= f.label :remove_image %> <%= f.check_box :remove_image %> <% end %>
Hvis albumet har et tilhørende bilde, viser vi det og gjengir en avkrysningsboks. Hvis denne avkrysningsboksen er satt, vil bildet bli fjernet. Merk at hvis feltet ditt er oppkalt photo_uid
, da vil den tilsvarende metoden for å fjerne vedlegg være remove_photo
. Enkelt, er det ikke?
Den eneste andre tingen å gjøre er å tillate remove_image
attributt i kontrolleren din:
params.require (: album) .permit (: title,: sanger,: image,: remove_image)
På dette stadiet virker alt bra, men vi kontrollerer ikke brukerens innspill i det hele tatt, noe som ikke er spesielt bra. Derfor la vi legge til valideringer for albummodellen:
validerer: tittel, tilstedeværelse: ekte validerer: sanger, tilstedeværelse: ekte validerer: bilde, tilstedeværelse: sant validates_property: width, of:: image, in: (0 ... 900)
validates_property
er Dragonfly-metoden som kan kontrollere ulike aspekter av vedlegget ditt: Du kan validere en fils utvidelse, MIME-type, størrelse osv..
La oss nå lage en generisk del for å gjengi feilene som ble funnet:
<% if object.errors.any? %><% end %>Følgende feil ble funnet:
<% object.errors.full_messages.each do |msg| %>
- <%= msg %>
<% end %>
Bruk denne delen i skjemaet:
<%= form_for @album do |f| %> <%= render 'shared/errors', object: @album %> <%#… %> <% end %>
Stil feltene med feil litt for å visuelt skildre dem:
.field_with_errors display: inline; etikett farge: rød; input bakgrunnsfargen: lightpink;
Etter å ha innført valideringer, løper vi inn enda et problem (ganske typisk scenario, eh?): Hvis brukeren har gjort feil mens du fyller ut skjemaet, må han eller hun velge filen igjen etter å ha klikket på Sende inn knapp.
Dragonfly kan også hjelpe deg med å løse dette problemet ved å bruke en retained_ *
skjult felt:
<%= f.hidden_field :retained_image %>
Ikke glem å tillate dette feltet også:
params.require (: album) .permit (: title,: singer,: image,: remove_image,: retained_image)
Nå vil bildet være vedvarende mellom forespørsler! Det eneste lille problemet er imidlertid at filopplastingsinngangen fortsatt vil vise meldingen "velg en fil", men dette kan løses med noe styling og et dash av JavaScript.
Bildene lastet opp av brukerne våre, kan ha svært forskjellige dimensjoner, noe som kan (og sannsynligvis vil) føre til en negativ innvirkning på nettstedets design. Du vil sannsynligvis skalere bilder ned til noen faste dimensjoner, og selvfølgelig er dette mulig ved å bruke bredde
og høyde
stiler. Dette er imidlertid ikke en optimal tilnærming: nettleseren må fortsatt laste ned fullstørrelsesbilder og krympe dem.
Et annet alternativ (som vanligvis er mye bedre) er å generere bildeminiatyrer med noen forhåndsdefinerte dimensjoner på serveren. Dette er veldig enkelt å oppnå med Dragonfly:
250x250
er selvsagt dimensjonene, mens #
er geometrien som betyr "endre størrelse og beskjære om nødvendig for å opprettholde aspektforholdet med senterets tyngdekraften". Du kan finne informasjon om andre geometrier på Dragonflys nettsted.
De tommel
Metoden drives av ImageMagick-en flott løsning for å lage og manipulere bilder. Derfor, for å se den fungerende demoen lokalt, må du installere ImageMagick (alle de store plattformene støttes).
Støtte for ImageMagick er aktivert som standard inne i Dragonfly's initializer:
plugin: imagemagick
Nå genereres miniatyrbilder, men de lagres ikke hvor som helst. Dette betyr at hver gang en bruker besøker albumsiden, blir miniatyrbildene regenerert. Det er to måter å overvinne dette problemet ved å generere dem etter at posten er lagret eller ved å utføre generasjon på fly.
Det første alternativet innebærer å innføre en ny kolonne for å lagre miniatyrbildet og justere dragonfly_accessor
metode. Opprett og bruk en ny migrering:
skinner g migrasjon add_image_thumb_uid_to_albums image_thumb_uid: strengskinner db: migrere
Modifiser nå modellen:
dragonfly_accessor: bildet gjør copy_to (: image_thumb) | a | a.thumb ('250x250 #') slutt dragonfly_accessor: image_thumb
Legg merke til at nå det første anropet til dragonfly_accessor
sender en blokk som faktisk genererer miniatyrbildet for oss og kopierer det til image_thumb
. Bruk bare image_thumb
metode i dine synspunkter:
<%= image_tag(album.image_thumb.url, alt: album.title) if album.image_thumb_stored? %>
Denne løsningen er den enkleste, men det anbefales ikke av de offisielle doksene, og hva er verre, når det skrives, virker det ikke med retained_ *
Enger.
Derfor, la meg vise deg et annet alternativ: generere miniatyrbilder i fly. Det innebærer å skape en ny modell og tilpasse Dragonfly's config-fil. Først, modellen:
skinner g modell Thumb uid: string jobb: streng rake db: migrere
De tommelen
bordet vil være vert for miniatyrbildene dine, men de vil bli generert på forespørsel. For å få dette til å skje, må vi omdefinere url
metode inne i Dragonfly initializer:
Dragonfly.app.configure gjør define_url gjør | app, jobb, opts | thumb = Thumb.find_by_job (job.signature) hvis tommelen app.datastore.url_for (thumb.uid,: scheme => 'https') annet app.server.url_for (jobb) slutten end_serve do | job, env | uid = job.store Thumb.create! (: uid => uid,: jobb => jobb.signatur) slutt # ... ende
Legg nå et nytt album og besøk rotsiden. Første gang du gjør det, blir følgende utskrift skrevet ut i loggene:
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 "
Dette betyr at miniatyrbildet genereres for oss av ImageMagick. Hvis du oppdaterer siden, vises denne linjen ikke lenger, noe som betyr at miniatyrbildet ble cached! Du kan lese litt mer om denne funksjonen på Dragonflys nettsted.
Du kan utføre nesten hvilken som helst manipulasjon av bildene dine etter at de ble lastet opp. Dette kan gjøres inne i after_assign
Ring tilbake. La oss for eksempel konvertere alle våre bilder til JPEG-format med 90% kvalitet:
dragonfly_accessor: image do after_assign | a | a.encode! ('jpg', '-quality 90') slutten
Det er mange flere handlinger du kan utføre: roter og beskjær bildene, koder med et annet format, skriv tekst på dem, bland med andre bilder (for eksempel å plassere et vannmerke) etc. For å se noen andre eksempler, se ImageMagick-delen på Dragonfly-nettstedet.
Selvfølgelig er hoveddelen av vårt musikalske nettsted sanger, så la oss legge til dem nå. Hver sang har en tittel og en musikalsk fil, og den tilhører et album:
skinner g modell Sangalbum: belongs_to title: string track_uid: strengskinner db: migrere
Koble til Dragonfly-metodene, som vi gjorde for Album
modell:
dragonfly_accessor: spor
Ikke glem å etablere en har mange
forhold:
has_many: sanger, avhengig:: ødelegge
Legg til nye ruter. En sang eksisterer alltid i omfanget av et album, så jeg vil gjøre disse ruterne nestet:
ressurser: album gjør ressurser: sanger, bare: [: ny,: opprett] slutt
Lag en veldig enkel kontroller (igjen, ikke glem å tillate spor
felt):
klasse 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
Vis sangene og en lenke for å legge til en ny:
<%= @album.title %>
av <%= @album.singer %>
<%= link_to 'Add song', new_album_song_path(@album) %><%= render @album.songs %>
Kode skjemaet:
Legg til sang til <%= @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 %>
Til slutt legger du til _sang delvis:
Her bruker jeg HTML5 lyd
tag, som ikke fungerer for eldre nettlesere. Så, hvis du har som mål å støtte slike nettlesere, bruk en polyfil.
Som du ser, er hele prosessen veldig enkel. Dragonfly bryr seg ikke akkurat hva slags fil du ønsker å laste opp; alt du trenger å gjøre er å gi en dragonfly_accessor
metode, legg til et riktig felt, tillat det, og gjeng en filinngangstag.
Når jeg åpner en spilleliste, forventer jeg å se ytterligere informasjon om hver sang, som dens varighet eller bithastighet. Selvfølgelig er denne informasjonen som standard ikke lagret hvor som helst, men vi kan ordne det ganske enkelt. Dragonfly lar oss gi ytterligere data om hver opplastede fil og hente den senere ved å bruke meta
metode.
Ting er imidlertid litt mer komplekse når vi jobber med lyd eller video, fordi å hente metadata er det behov for en spesiell perle streamio-ffmpeg. Denne perlen er igjen avhengig av FFmpeg, så for å kunne fortsette må du installere den på PCen.
Legg til streamio-ffmpeg
inn i det Gemfile:
perle 'streamio-ffmpeg'
Installer den:
bunt installasjon
Nå kan vi ansette det samme after_assign
tilbakeringing allerede sett i forrige seksjoner:
dragonfly_accessor: track do after_assign do | a | sang = 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 end-enden
Merk at her bruker jeg en sti
metode, ikke url
, fordi på dette punktet jobber vi med en tempfile. Deretter pakker vi bare ut sangens varighet (konvertere den til minutter og sekunder med ledende nuller) og bitrate (konvertere den til kilobytes per sekund).
Til slutt, vis metadata i visningen:
Hvis du sjekker innholdet på offentlig / system / øyenstikker mappe (standardplassering som vert for opplastingene), vil du legge merke til noen .YML filer - de lagrer all metainformasjon i YAML-format.
Det siste emnet vi skal dekke i dag, er hvordan du klargjør søknaden din før du distribuerer til Heroku skyplattform. Hovedproblemet er at Heroku ikke tillater deg å lagre egendefinerte filer (som opplastinger), så vi må stole på en skytholdingsservice som Amazon S3. Heldigvis kan Dragonfly integreres med det enkelt.
Alt du trenger å gjøre er å registrere en ny konto hos AWS (hvis du ikke allerede har det), opprett en bruker med tillatelse til å få tilgang til S3-buckets, og skriv ned brukerens nøkkelpar på et trygt sted. Du kan bruke et rotnøkkelpar, men dette er egentlig ikke anbefalt. Til slutt, lag en S3 bøtte.
Gå tilbake til programmet Rails, sett inn en ny perle:
gruppe: produksjon gjør perle 'dragonfly-s3_data_store' slutten
Installer den:
bunt installasjon
Deretter justerer du Dragonfly-konfigurasjonen for å bruke S3 i et produksjonsmiljø:
hvis 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' annet datastore: root_path: Rails.root.join ('public / system / dragonfly', Rails.env), server_root: Rails.root.join ('public') slutten
Å skaffe ENV
variabler på Heroku, bruk denne kommandoen:
heroku config: legg til SOME_KEY = SOME_VALUE
Hvis du ønsker å teste integrasjon med S3 lokalt, kan du bruke en perle som dotenv-skinner til å håndtere miljøvariabler. Husk imidlertid at ditt AWS nøkkelpar må ikke være offentlig eksponert!
Et annet lite problem jeg har kjørt inn i mens du distribuerer til Heroku, var fraværet av FFmpeg. Saken er at når et nytt Heroku-program blir opprettet, har det et sett av tjenester som vanligvis brukes (for eksempel, ImageMagick er tilgjengelig som standard). Andre tjenester kan installeres som Heroku addons eller i form av buildpacks. For å legge til en FFmpeg buildpack, kjør følgende kommando:
heroku buildpacks: legg til https://github.com/HYPERHYPER/heroku-buildpack-ffmpeg.git
Nå er alt klart, og du kan dele din musikalske applikasjon med verden!
Dette var en lang reise, ikke sant? I dag har vi diskutert Dragonfly-en løsning for filopplasting i Rails. Vi har sett sitt grunnleggende oppsett, noen konfigurasjonsalternativer, miniatyr generering, behandling og metadata lagring. Vi har også integrert Dragonfly med Amazon S3-tjenesten og utarbeidet vårt program for distribusjon på produksjon.
Selvfølgelig har vi ikke diskutert alle aspekter av Dragonfly i denne artikkelen, så sørg for å bla gjennom sitt offisielle nettsted for å finne omfattende dokumentasjon og nyttige eksempler. Hvis du har andre spørsmål eller er fast med noen kodeeksempler, ikke nøl med å kontakte meg.
Takk for at du bodde hos meg og ser deg snart!