Laster opp med skinner og Carrierwave

Dette er en annen artikkel i serien "Opplasting med skinner". I dag skal vi møte Carrierwave-en av de mest populære filopplastingsløsningene for Rails. Jeg liker Carrierwave fordi det er lett å komme i gang, det har mange funksjoner ut av esken, og det gir dusinvis av "hvordan" artikler skrevet av medlemmer av samfunnet, slik at du ikke vil gå seg vill.

I denne artikkelen lærer du hvordan du:

  • Integrer Carrierwave i din Rails app
  • Legg til valideringer
  • Fortsett filer på tvers av forespørsler
  • Fjern filer
  • Generer miniatyrbilder
  • Last opp filer fra eksterne steder
  • Introduser flere filopplastinger
  • Legg til støtte for skylagring

Kildekoden for denne artikkelen er tilgjengelig på GitHub. Liker å lese!

Legge grunnlaget

Som alltid, begynn med å opprette et nytt Rails-program:

Skinner nye UploadingWithCarrierwave -T

For denne demoen bruker jeg Rails 5.0.2. Vær oppmerksom på at Carrierwave 1 kun støtter Rails 4+ og Ruby 2. Hvis du fremdeles kjører på Rails 3, kobler du deretter Carrierwave-versjonen 0.11.

For å se Carrierwave i aksjon, skal vi lage en veldig enkel blogging-applikasjon med en såle Post modell. Den vil ha følgende hovedattributter:

  • tittel (string)
  • kropp (tekst)
  • bilde (string) - dette feltet skal inneholde et bilde (en fil navn, for å være presis) knyttet til innlegget

Generer og bruk en ny migrering:

skinner g modell Post tittel: streng body: tekst bilde: streng skinner db: migrere

Sett opp noen ruter:

config / routes.rb

ressurser: innlegg rot til: 'innlegg # indeks'

Lag også en veldig grunnleggende kontroller:

posts_controller.rb

klasse PostsController < ApplicationController before_action :set_post, only: [:show, :edit, :update] def index @posts = Post.order('created_at DESC') end def show end def new @post = Post.new end def create @post = Post.new(post_params) if @post.save redirect_to posts_path else render :new end end def edit end def update if @post.update_attributes(post_params) redirect_to post_path(@post) else render :edit end end private def post_params params.require(:post).permit(:title, :body, :image) end def set_post @post = Post.find(params[:id]) end end

La oss nå lage index utsikt:

visninger / innlegg / index.html.erb

innlegg

<%= link_to 'Add post', new_post_path %> <%= render @posts %>

Og den tilsvarende delvise:

visninger / innlegg / _post.html.erb

<%= link_to post.title, post_path(post) %>

<%= truncate(post.body, length: 150) %>

<%= link_to 'Edit', edit_post_path(post) %>


Her bruker jeg Rails avkorte Metode for å vise bare de første 150 symbolene fra innlegget. Før vi lager andre visninger og et skjema delvis, la oss først integrere Carrierwave i programmet.

Integrerer Carrierwave

Sett inn en ny perle i Gemfile:

Gemfile

perle 'carrierwave', '~> 1.0'

Løpe:

bunt installasjon

Carrierwave lagrer sin konfigurasjon innenfor opplastere som er inkludert i modellene dine. For å generere en opplastingsprogram, bruk følgende kommando:

skinner genererer opplastingsbilde

Nå inne app / opplastere, Du finner en ny fil som heter image_uploader.rb. Vær oppmerksom på at den har noen nyttige kommentarer og eksempler, så du kan bruke den til å komme i gang. I denne demoen bruker vi ActiveRecord, men Carrierwave har også støtte for Mongoid, Sequel og DataMapper..

Deretter må vi inkludere eller mount denne opplasteren i modellen:

modeller / post.rb

mount_uploader: bilde, ImageUploader

Opplasteren har allerede sane standardinnstillinger, men i det minste må vi velge hvor de opplastede filene skal lagres. For nå, la oss ansette fillagring:

opplastere / image_uploader.rb

lagring: fil

Som standard vil filer plasseres inne i offentlige / opplastinger katalog, så det er best å ekskludere det fra versjonskontrollsystemet:

.gitignore

offentlige / opplastinger

Du kan også endre store_dir Metoden i opplasteren din for å velge et annet sted.

På dette tidspunktet kan vi opprette en ny visning og et skjema delvis for å starte opplasting av filer:

visninger / innlegg / new.html.erb

Legg til innlegg

<%= render 'form', post: @post %>

visninger / innlegg / _form.html.erb

<%= form_for post do |f| %> 
<%= f.label :title %> <%= f.text_field :title %>
<%= f.label :body %> <%= f.text_area :body %>
<%= f.label :image %> <%= f.file_field :image %>
<%= f.submit %> <% end %>

Legg merke til at PostsController trenger ikke å endres som vi allerede har tillatt bilde Egenskap.

Til slutt, opprett redigeringsvisningen:

visninger / innlegg / edit.html.erb

Rediger innlegg

<%= render 'form', post: @post %>

Det er det! Du kan starte opp serveren og prøve å lage et innlegg med et bilde. Problemet er at dette bildet ikke er synlig hvor som helst, så la oss fortsette til neste del og legge til en visningsside!

Viser bilder

Så den eneste utsikten vi ikke har opprettet ennå er vise fram. Legg til det nå:

visninger / innlegg / show.html.erb

<%= link_to 'All posts', posts_path %> 

<%= @post.title %>

<%= image_tag(@post.image.url, alt: 'Image') if @post.image? %>

<%= @post.body %>

<%= link_to 'Edit', edit_post_path(@post) %>

Som du kan se, er det veldig enkelt å vise et vedlegg: alt du trenger å gjøre er å si @ post.image.url å ta et bilde URL. For å få en bane til filen, bruk current_path metode. Legg merke til at Carrierwave også gir en bilde? Metode for oss å sjekke om et vedlegg er til stede i det hele tatt (den bilde Metoden selv vil aldri komme tilbake nil, selv om filen ikke er tilstede).

Nå, etter å ha navigert til et innlegg, bør du se et bilde, men det kan virke for stort. Tross alt begrenser vi ikke dimensjoner hvor som helst. Selvfølgelig kunne vi ha nedskalert bildet med noen CSS-regler, men det er mye bedre å generere en miniatyrbilde etter at filen har blitt lastet opp. Dette krever imidlertid noen ekstra trinn.

Genererer miniatyrbilder

For å beskjære og skalere bilder trenger vi et eget verktøy. Ut av boksen har Carrierwave støtte for RMagick og MiniMagick edelstener som i sin tur brukes til å manipulere bilder ved hjelp av ImageMagick. ImageMagick er en åpen kildekode-løsning som lar deg redigere eksisterende bilder og generere nye, så før du fortsetter må du laste ned og installere det. Deretter er du fri til å velge en av de to juvelene. Jeg holder fast med MiniMagick, fordi det er mye enklere å installere og det har bedre støtte: 

Gemfile

perle 'mini_magick'

Løpe:

bunt installasjon

Deretter inkluderer MiniMagick i opplasteren din:

opplastere / image_uploader.rb

Inkluder CarrierWave :: MiniMagick

Nå trenger vi bare å introdusere en ny versjon til opplasteren vår. Konseptet av versjoner (eller stiler) brukes i mange filopplastingsbiblioteker; det betyr bare at flere filer basert på det opprinnelige vedlegget vil bli opprettet med for eksempel forskjellige dimensjoner eller formater. Sett inn en ny versjon som heter tommel:

opplastere / image_uploader.rb

versjon: tommelen gjør prosessen resize_to_fill: [350, 350] end

Du kan ha så mange versjoner som du vil, og i tillegg kan versjoner også bygges på toppen av andre:

opplastere / image_uploader.rb

versjon: small_thumb, from_version:: tommelen gjør prosessen resize_to_fill: [20, 20] end

Hvis du allerede har lastet opp noen bilder, vil de ikke ha miniatyrbilder tilgjengelig. Dette er ikke et problem, selv om du kan gjenopprette dem fra Rails-konsollen:

skinner c Post.find_each | post | post.image.recreate_versions! (: thumb) hvis post.image?

Til slutt, vis miniaturen din med en lenke til det opprinnelige bildet:

visninger / innlegg / show.html.erb

<%= link_to(image_tag(@post.image.thumb.url, alt: 'Image'), @post.image.url, target: '_blank') if @post.image? %> 

Start serveren og følg resultatet!

Legge til valideringer

For tiden fungerer opplastingen, men vi bekrefter ikke brukerinngang, noe som selvsagt er dårlig. Så lenge vi bare vil arbeide med bilder, la oss hviteliste .png, .jpg og .gif-utvidelser:

opplastere / image_uploader.rb

def extension_whitelist% w (jpg jpeg gif png) slutten

Du kan også legge til innholdstype sjekker ved å definere en content_type_whitelist metode:

opplastere / image_uploader.rb

def content_type_whitelist / image \ // end

Alternativt er det mulig å svarte noen filtyper, for eksempel kjørbare, ved å definere content_type_blacklist metode.

Bortsett fra å sjekke en fils type og utvidelse, la oss håndheve den til mindre enn 1 megabyte. For å gjøre det, krever vi en ekstra perle som støtter filvalideringer for ActiveModel:

Gemfile

perle 'file_validators'

Installer den:

bunt installasjon

Nå introdusere ønskede valideringer (merk at jeg også legger til sjekker for tittel og kropp egenskaper):

modeller / post.rb

validerer: tittel, tilstedeværelse: sann, lengde: minimum: 2 validerer: kropp, tilstedeværelse: true validates: image, file_size: mindre_than: 1.megabytes

Den neste tingen å gjøre er å legge til I18n-oversettelser for Carrierwaves feilmeldinger:

konfig / steder / en.yml

en: feil: meldinger: carrierwave_processing_error: "Kan ikke endre størrelsen på bildet." carrierwave_integrity_error: "Ikke et bilde." carrierwave_download_error: "Kunne ikke laste ned bilde." extension_whitelist_error: "Du har ikke lov til å laste opp% extension filer, tillatte typer:% allowed_types" extension_blacklist_error: "Du har ikke lov til å laste opp% extension filer, forbudte typer:% prohibited_types"

For øyeblikket viser vi ikke valideringsfeil hvor som helst, så la oss opprette en delt del:

visninger / delt / _errors.html.erb

<% if object.errors.any? %> 

Noen feil ble funnet:

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

Bruk denne delen i skjemaet:

visninger / innlegg / _form.html.erb

<%= render 'shared/errors', object: post %>

Prøv nå å laste opp noen ugyldige filer og observere resultatet. Det skal fungere, men hvis du velger en gyldig fil og ikke fyller inn tittelen eller kroppen, vil kontrollene fortsatt mislykkes, og en feil vil bli vist. Filfeltet blir imidlertid slettet, og brukeren må velge bildet igjen, noe som ikke er veldig praktisk. For å fikse det, må vi legge til et annet felt i skjemaet.

Vedvarende filer over forespørsler

Vedvarende filer på tvers av redisplays er faktisk ganske enkelt. Alt du trenger å gjøre er å legge til et nytt skjult felt og tillate det inne i kontrolleren:

visninger / delt / _form.html.erb

<%= f.label :image %> <%= f.file_field :image %>
<%= f.hidden_field :image_cache %>

posts_controller.rb

params.require (: post) .permit (: title,: body,: image,: image_cache)

image_cache vil bli befolket automatisk og bildet vil ikke gå tapt. Det kan også være nyttig å vise et miniatyrbilde, slik at brukeren forstår at bildet ble behandlet vellykket: 

visninger / delt / _form.html.erb

<% if post.image? %> <%= image_tag post.image.thumb.url %> <% end %>

Fjerner bilder

En annen svært vanlig funksjon er evnen til å fjerne vedlagte filer når du redigerer en plate. Med Carrierwave er det ikke et problem å implementere denne funksjonen. Legg til en ny boks i skjemaet:

visninger / delt / _form.html.erb

<% if post.image? %> <%= image_tag post.image.thumb.url %> 
<%= label_tag :remove_image do %> Fjern bilde <%= f.check_box :remove_image %> <% end %>
<% end %>

Og tillat det remove_image Egenskap:

posts_controller.rb

params.require (: post) .permit (: title,: body,: image,: remove_image,: image_cache)

Det er det! Hvis du vil fjerne et bilde manuelt, bruker du remove_image! metode:

@ post.remove_image!

Laster opp fra en ekstern plassering

Carrierwave gir også en veldig kul funksjon ut av boksen: muligheten til å laste opp filer fra eksterne steder ved deres nettadresse. La oss introdusere denne evnen nå ved å legge til et nytt felt og tillate det tilsvarende attributtet: 

visninger / delt / _form.html.erb

<%= f.text_field :remote_image_url %> Skriv inn URL til et bilde

posts_controller.rb

params.require (: post) .permit (: title,: body,: image,: remove_image,: image_cache,: remote_image_url)

Hvor kult er det? Du trenger ikke å gjøre noen endringer i det hele tatt, og du kan teste denne funksjonen med en gang!

Arbeider med flere opplastinger

Anta at vi vil at vårt innlegg skal ha flere vedlegg tilgjengelig. Med det nåværende oppsettet er det ikke mulig, men heldigvis støtter Carrierwave også et slikt scenario. For å implementere denne funksjonen må du legge til enten et serialisert felt (for SQLite) eller et JSON-felt (for Postgres eller MySQL). Jeg foretrekker sistnevnte alternativ, så la oss bytte til en ny databaseadapter nå. Fjern sqlite3 perlen fra Gemfile og legg til pg i stedet:

Gemfile

perle 'pg'

Installer den:

bunt installasjon

Endre databasekonfigurasjonen slik:

config / database.yml

standard: & standardadapter: postgresql pool: 5 timeout: 5000 utvikling: <<: *default database: upload_carrier_dev username: 'YOUR_USER' password: 'YOUR_PASSWORD' host: localhost

Opprett den tilsvarende Postgres-databasen, og generer og bruk deretter overføringen:

skinner g migrasjon add_attachments_to_posts vedlegg: json skinner db: migrere

Hvis du foretrekker å holde fast med SQLite, følg instruksjonene som er oppført i Carrierwaves dokumentasjon.

Monter nå opplastingene (merk pluralformen!):

modell / post.rb

mount_uploaders: vedlegg, ImageUploader

Jeg bruker den samme opplasteren for vedlegg, men selvfølgelig kan du generere en ny med en annen konfigurasjon.

Legg til flere filfelt i skjemaet ditt:

visninger / delt / _form.html.erb

<%= f.label :attachments %> <%= f.file_field :attachments, multiple: true %>

Så lenge som vedlegg feltet skal inneholde en matrise, bør det tillates på følgende måte:

posts_controller.rb

params.require (: post) .permit (: title,: body,: image,: remove_image,: image_cache,: remote_image_url, vedlegg: [])

Til slutt kan du iterere over innleggets vedlegg og vise dem som vanlig:

visninger / delt / show.html.erb

<% if @post.attachments? %> 
    <% @post.attachments.each do |attachment| %>
  • <%= link_to(image_tag(attachment.thumb.url, alt: 'Image'), attachment.url, target: '_blank') %>
  • <% end %>
<% end %>

Merk at hvert vedlegg skal ha en miniatyrbilde som konfigurert i vår ImageUploader. Hyggelig!

Bruk av Cloud Storage

Stikker med fillagring er ikke alltid praktisk og / eller mulig, for eksempel på Heroku er det ikke mulig å lagre egendefinerte filer. Derfor kan du spørre hvordan du skal gifte deg med Carrierwave med Amazon S3 skylagring? Vel, det er også en ganske enkel oppgave. Carrierwave avhenger av tåke-aws perlen for å implementere denne funksjonen:

Gemfile

perle "tåke-aws"

Installer den:

bunt installasjon

La oss lage en initialiserer for Carrierwave og konfigurere skylagringsplassen globalt:

konfig / initializers / carrierwave.rb

CarrierWave.configure do | config | config.fog_provider = 'tåke / aws' config.fog_credentials = leverandør: 'AWS', aws_access_key_id: ENV ['S3_KEY'], aws_secret_access_key: ENV ['S3_SECRET'], region: ENV ['S3_REGION'], config. fog_directory = ENV ['S3_BUCKET'] slutten

Det finnes noen andre alternativer, som finnes i dokumentasjonen.

Jeg bruker dotenv-skinner perlen for å sette miljøvariabler på en sikker måte, men du kan velge et annet alternativ. Sørg imidlertid for at S3-nøkkelparet ikke er tilgjengelig offentlig, fordi ellers kan noen laste opp noe til bøtte!

Deretter erstattes lagring: fil linje med:

opplastere / image_uploader.rb

lagring: tåke

Bortsett fra S3, støtter Carrierwave opplastinger til Google Storage and Rackspace. Disse tjenestene er også enkle å sette opp.

Konklusjon

Dette er det for i dag! Vi har dekket alle de viktigste funksjonene i Carrierwave, og nå kan du begynne å bruke det i dine prosjekter. Det har noen ekstra alternativer tilgjengelig, så gå gjennom dokumentasjonen.

Hvis du sitter fast, ikke nøl med å legge inn dine spørsmål. Det kan også være nyttig å ta en titt på Carrierwaves wiki, som er vert for nyttige "hvordan å" artikler som svarer på mange vanlige spørsmål.

Så jeg takker deg for at du bodde hos meg og lykkelig koding!