Uppladdning med skenor och Carrierwave

Det här är en annan artikel i serien "Uploading with Rails". Idag kommer vi att träffa Carrierwave-en av de mest populära filuppladdningslösningarna för Rails. Jag gillar Carrierwave eftersom det är lätt att komma igång, det har många funktioner ur lådan, och det ger dussintals "hur" artiklar som skrivs av medlemmarna i samhället, så du kommer inte gå vilse.

I den här artikeln lär du dig att:

  • Integrera Carrierwave i din Rails app
  • Lägg till valideringar
  • Fortsätt filer över önskemål
  • Ta bort filer
  • Skapa miniatyrer
  • Ladda upp filer från avlägsna platser
  • Introducera flera filuppladdningar
  • Lägg till stöd för molnlagring

Källkoden för den här artikeln finns på GitHub. Njut av att läsa!

Lägger grundarna

Börja med att skapa en ny Rails-applikation som alltid:

spårar nya UploadingWithCarrierwave -T

För den här demo använder jag Rails 5.0.2. Observera att Carrierwave 1 endast stöder Rails 4+ och Ruby 2. Om du fortfarande kör på Rails 3, kopplar du sedan upp Carrierwave version 0.11.

För att se Carrierwave i åtgärd kommer vi att skapa en mycket enkel bloggapplikation med en sål Posta modell. Den kommer att ha följande huvudattribut:

  • titel (sträng)
  • kropp (text)
  • bild (sträng) -Detta fält kommer att innehålla en bild (en fils namn, för att vara exakt) bifogad posten

Generera och tillämpa en ny migrering:

rails g model Posttitel: strängkropp: textbild: strängskenor db: migrera

Ställ in några vägar:

config / routes.rb

resurser: inlägg root till: 'inlägg # index'

Skapa också en mycket grundläggande kontroller:

posts_controller.rb

klass 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

Nu låt oss hantla index se:

visningar / inlägg / index.html.erb

inlägg

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

Och motsvarande del:

visningar / inlägg / _post.html.erb

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

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

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


Här använder jag Rails stympa Metod för att bara visa de första 150 symbolerna från posten. Innan vi skapar andra visningar och ett formulär delvis, låt oss först integrera Carrierwave i programmet.

Integrerar Carrierwave

Släpp in en ny pärla i Gemfile:

Gemfile

pärla "carrierwave", "~> 1.0"

Springa:

buntinstallation

Carrierwave lagrar sin konfiguration inuti uppladdare som ingår i dina modeller. För att skapa en uppladdare, använd följande kommando:

rails generera uploader Image

Nu inuti app / uppladdare, Du hittar en ny fil som heter image_uploader.rb. Observera att det har några användbara kommentarer och exempel, så du kan använda den för att komma igång. I denna demo kommer vi att använda ActiveRecord, men Carrierwave har också stöd för Mongoid, Sequel och DataMapper.

Därefter måste vi inkludera eller montera den här uppladdaren i modellen:

modeller / post.rb

mount_uploader: bild, ImageUploader

Uppladdaren har redan sanna standardinställningar, men åtminstone måste vi välja var de uppladdade filerna ska lagras. För närvarande, låt oss anställa fillagring:

uppladdare / image_uploader.rb

lagring: fil

Som standard kommer filer att placeras inuti offentliga / uppladdningar katalog, så det är bäst att utesluta det från versionskontrollsystemet:

.gitignore

offentliga / uppladdningar

Du kan också ändra store_dir metod inom din uppladdare för att välja någon annan plats.

Vid denna tidpunkt kan vi skapa en ny vy och ett formulär delvis för att börja ladda upp filer:

visningar / inlägg / new.html.erb

Lägg till inlägg

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

visningar / inlägg / _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 %>

Observera att PostsController behöver inte ändras som vi redan har tillåtit bild attribut.

Slutligen skapa redigeringsvyn:

visningar / inlägg / edit.html.erb

Redigera inlägg

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

Det är allt! Du kan starta servern och försöka skapa ett inlägg med en bild. Problemet är att bilden inte är synlig någonstans, så låt oss fortsätta till nästa avsnitt och lägga till en visningssida!

Visar bilder

Så den enda vy som vi inte har skapat ännu är show. Lägg till det nu:

visningar / inlägg / 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 är det väldigt lätt att visa en bilaga: allt du behöver göra är att säga @ post.image.url att ta tag i en bilds URL. För att få en sökväg till filen, använd current_path metod. Observera att Carrierwave också ger en bild? metod för oss att kontrollera om det finns en bilaga alls (den bild Metoden själv kommer aldrig att återvända noll, även om filen inte är närvarande).

Nu, efter att du navigerat till ett inlägg, bör du se en bild, men det kan verka för stor: trots allt begränsar vi inte dimensionerna någonstans. Självklart kan vi ha minskat bilden med några CSS-regler, men det är mycket bättre att skapa en miniatyrbild efter att filen har laddats upp. Detta kräver dock några ytterligare steg.

Generera miniatyrbilder

För att beskära och skala bilder behöver vi ett separat verktyg. Utanför lådan har Carrierwave stöd för RMagick och MiniMagick ädelstenar som i sin tur används för att manipulera bilder med hjälp av ImageMagick. ImageMagick är en öppen källkodslösning som gör att du kan redigera befintliga bilder och skapa nya, så innan du fortsätter måste du hämta och installera det. Därefter är du fri att välja någon av de två pärlorna. Jag håller med MiniMagick, eftersom det är mycket lättare att installera och det har bättre stöd: 

Gemfile

pärla "mini_magick"

Springa:

buntinstallation

Lägg sedan till MiniMagick i din uppladdare:

uppladdare / image_uploader.rb

Inkludera CarrierWave :: MiniMagick

Nu behöver vi helt enkelt introducera en ny version till vår uppladdare. Konceptet av versioner (eller stilar) används i många filuppladdningsbibliotek; det betyder helt enkelt att ytterligare filer baserat på den ursprungliga bilagan kommer att skapas med till exempel olika dimensioner eller format. Presentera en ny version som heter tumme:

uppladdare / image_uploader.rb

version: tummen gör processen resize_to_fill: [350, 350] slutet

Du kan ha så många versioner som du vill, och dessutom kan versioner även byggas utöver andra:

uppladdare / image_uploader.rb

version: small_thumb, from_version:: tummen gör processen resize_to_fill: [20, 20] slutet

Om du redan har laddat upp några bilder kommer de inte att ha miniatyrer tillgängliga. Detta är inte ett problem, eftersom du kan skapa dem igen från spårningskonsolen:

rails c Post.find_each | post | post.image.recreate_versions! (: thumb) om post.image?

Slutligen, visa din miniatyrbild med en länk till originalbilden:

visningar / inlägg / show.html.erb

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

Starta servern och följ resultatet!

Lägga till valideringar

För närvarande fungerar vår uppladdning, men vi godkänner inte alls användarinmatning, vilket är självklart dåligt. Så länge vi bara vill arbeta med bilder, låt oss vitlista .png, .jpg och .gif-tillägg:

uppladdare / image_uploader.rb

def extension_whitelist% w (jpg jpeg gif png) slut

Du kan också lägga till innehållstypskontroller genom att definiera en content_type_whitelist metod:

uppladdare / image_uploader.rb

def content_type_whitelist / image \ // end

Alternativt är det möjligt att svartlista vissa filtyper, till exempel körbara filer, genom att definiera content_type_blacklist metod.

Bortsett från att du kontrollerar en fils typ och förlängning, låt oss genomdriva det för att vara mindre än 1 megabyte. För att göra det behöver vi en extra pärla som stöder filvalideringar för ActiveModel:

Gemfile

pärla "file_validators"

Installera det:

buntinstallation

Nu introducera önskade valideringar (notera att jag också lägger till kontroller för titel och kropp attribut):

modeller / post.rb

validerar: titel, närvaro: sann, längd: minimum: 2 validerar: kropp, närvaro: true validates: image, file_size: less_than: 1.megabytes

Nästa sak att göra är att lägga till I18n-översättningar för Carrierwaves felmeddelanden:

config / locales / en.yml

sv: fel: meddelanden: carrierwave_processing_error: "Kan inte ändra storlek på bild." carrierwave_integrity_error: "Inte en bild." carrierwave_download_error: "Kunde inte ladda ner bild." extension_whitelist_error: "Du får inte ladda upp% extension -filer, tillåtna typer:% allowed_types" extension_blacklist_error: "Du får inte ladda upp% extension -filer, förbjudna typer:% prohibited_types"

För närvarande visar vi inte valideringsfel någonstans, så låt oss skapa en delad del:

visningar / delad / _errors.html.erb

<% if object.errors.any? %> 

Några fel hittades:

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

Anlita detta partiellt inuti formuläret:

visningar / inlägg / _form.html.erb

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

Försök nu ladda upp några ogiltiga filer och observera resultatet. Det ska fungera, men om du väljer en giltig fil och inte fyller i titeln eller kroppen, kommer kontrollerna fortfarande att misslyckas och ett fel kommer att visas. Filfältet kommer emellertid att rensas ut och användaren måste välja bilden igen, vilket inte är särskilt bekvämt. För att fixa det måste vi lägga till ett annat fält i formuläret.

Hållbara filer över begäranden

Det är faktiskt ganska lätt att behålla filer över formredigeringar. Allt du behöver göra är att lägga till ett nytt gömt fält och tillåta det inom kontrollenheten:

visningar / delad / _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)

Nu den image_cache kommer att fyllas i automatiskt och bilden kommer inte att gå vilse. Det kan vara tillrådligt att visa en miniatyrbild så att användaren förstår att bilden har bearbetats framgångsrikt: 

visningar / delad / _form.html.erb

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

Ta bort bilder

En annan mycket vanlig funktion är möjligheten att ta bort bifogade filer när du redigerar en post. Med Carrierwave är det inte ett problem att implementera denna funktion. Lägg till en ny kryssruta i formuläret:

visningar / delad / _form.html.erb

<% if post.image? %> <%= image_tag post.image.thumb.url %> 
<%= label_tag :remove_image do %> Ta bort bilden <%= f.check_box :remove_image %> <% end %>
<% end %>

Och tillåta remove_image attribut:

posts_controller.rb

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

Det är allt! Om du vill ta bort en bild manuellt använder du remove_image! metod:

@ post.remove_image!

Överföring från ett fjärrplats

Carrierwave ger också en mycket cool funktion ur lådan: möjligheten att ladda upp filer från avlägsna platser via deras webbadress. Låt oss introducera denna förmåga nu genom att lägga till ett nytt fält och tillåta motsvarande attribut: 

visningar / delad / _form.html.erb

<%= f.text_field :remote_image_url %> Ange URL till en bild

posts_controller.rb

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

Hur coolt är inte det? Du behöver inte göra några förändringar alls, och du kan testa denna funktion direkt!

Arbetar med flera uppladdningar

Antag att vi vill att vårt inlägg ska ha flera bilagor tillgängliga. Med den nuvarande inställningen är det inte möjligt, men lyckligtvis Carrierwave stöder också ett sådant scenario. För att implementera denna funktion måste du lägga till antingen ett seriellt fält (för SQLite) eller ett JSON-fält (för Postgres eller MySQL). Jag föredrar det senare alternativet, så låt oss byta till en ny databasadapter nu. Ta bort sqlite3-pärlan från Gemfile och lägg till pg istället:

Gemfile

pärla 'pg'

Installera det:

buntinstallation

Ändra databaskonfigurationen så här:

config / database.yml

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

Skapa motsvarande Postgres-databas, och generera och tillämpa migreringen:

rails g migration add_attachments_to_posts bilagor: json rails db: migrera

Om du föredrar att hålla fast vid SQLite följer du instruktionerna i Carrierwaves dokumentation.

Montera nu uppladdningsenheterna (notera pluralformen!):

modell / post.rb

mount_uploaders: bilagor, ImageUploader

Jag använder samma uppladdare för bilagor, men självklart kan du skapa en ny med en annan konfiguration.

Lägg till flervalsfältet i ditt formulär:

visningar / delad / _form.html.erb

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

Så länge som bilagor fältet kommer att innehålla en matris, bör det tillåtas på följande sätt:

posts_controller.rb

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

Slutligen kan du iterera över inläggets bilagor och visa dem som vanligt:

visningar / delad / 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 %>

Observera att varje bilaga kommer att ha en miniatyrbild som konfigurerad i vår ImageUploader. Trevlig!

Använda Cloud Storage

Stickning med fillagring är inte alltid bekvämt och / eller möjligt, till exempel på Heroku är det inte möjligt att lagra anpassade filer. Därför kan du fråga hur man gifter sig med Carrierwave med Amazon S3-molnlagring? Tja, det är också en ganska lätt uppgift. Carrierwave beror på dimma-aws-pärlan för att genomföra denna funktion:

Gemfile

pärla "dimma-aws"

Installera det:

buntinstallation

Låt oss skapa en initializer för Carrierwave och konfigurera molnlagret globalt:

config / initializers / carrierwave.rb

CarrierWave.configure do | config | config.fog_provider = 'dimma / aws' config.fog_credentials = leverantö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'] slutet

Det finns några andra alternativ, som finns i dokumentationen.

Jag använder dotenv-skenerns pärla för att ställa miljövariablerna på ett säkert sätt, men du kan välja något annat alternativ. Se dock till att ditt S3-nyckelpar inte är tillgängligt offentligt, för annars kan någon ladda upp något till din hink!

Nästa, ersätt lagring: fil linje med:

uppladdare / image_uploader.rb

lagring: dimma

Bortsett från S3, stödjer Carrierwave uppladdningar till Google Storage and Rackspace. Dessa tjänster är lätta att installera också.

Slutsats

Detta är det för idag! Vi har täckt alla viktiga funktioner i Carrierwave, och nu kan du börja använda det i dina projekt. Det har några ytterligare alternativ tillgängliga, så bläddra i dokumentationen.

Om du är fast, tveka inte att skicka dina frågor. Det kan också vara bra att ta en titt på Carrierwaves wiki, som är värd för användbara "hur man" artiklar som svarar på många vanliga frågor.

Så jag tackar dig för att du bodde med mig och glad kodning!