Uploaden met Rails en Carrierwave

Dit is een ander artikel in de serie 'Uploaden met rails'. Vandaag gaan we Carrierwave ontmoeten, een van de populairste oplossingen voor het uploaden van bestanden voor Rails. Ik hou van Carrierwave omdat het gemakkelijk is om te beginnen, het heeft veel functies uit de doos en het biedt tientallen "how to" -artikelen geschreven door de leden van de community, zodat je niet verdwaalt.

In dit artikel leert u hoe u:

  • Integreer Carrierwave in uw Rails-app
  • Validaties toevoegen
  • Houd bestanden gedurende verschillende aanvragen aan
  • Verwijder bestanden
  • Genereer miniaturen
  • Upload bestanden van externe locaties
  • Introduceer meerdere bestandsuploads
  • Ondersteuning toevoegen voor cloudopslag

De broncode voor dit artikel is beschikbaar op GitHub. Veel plezier met lezen!

De fundamenten leggen

Zoals altijd, begin met het maken van een nieuwe Rails-applicatie:

rails nieuw UploadingWithCarrierwave -T

Voor deze demo gebruik ik Rails 5.0.2. Houd er rekening mee dat Carrierwave 1 alleen Rails 4+ en Ruby 2 ondersteunt. Als u nog steeds op Rails 3 rijdt, sluit dan Carrierwave-versie 0.11 aan..

Om Carrierwave in actie te zien, gaan we een heel eenvoudige blogtoepassing maken met een zool Post model. Het heeft de volgende hoofdattributen:

  • titel (draad)
  • lichaam (tekst)
  • beeld (draad) - dit veld zal een afbeelding bevatten (de naam van een bestand, om precies te zijn) aan het bericht

Genereer en pas een nieuwe migratie toe:

rails g model Titel bericht: string body: tekstafbeelding: stringrails db: migreren

Stel enkele routes in:

config / routes.rb

middelen: berichten root naar: 'posts # index'

Maak ook een erg eenvoudige controller:

posts_controller.rb

class 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

Laten we nu de inhoudsopgave uitzicht:

views / berichten / index.html.erb

berichten

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

En de bijbehorende gedeeltelijke:

views / berichten / _post.html.erb

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

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

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


Hier gebruik ik de Rails afknotten methode om alleen de eerste 150 symbolen van het bericht weer te geven. Voordat we andere weergaven en een gedeeltelijk formulier maken, laten we eerst Carrierwave integreren in de toepassing.

Carrierwave integreren

Doe een nieuw juweel in de Gemfile:

Gemfile

gem 'carrierwave', '~> 1.0'

Rennen:

bundel installeren

Carrierwave slaat de configuratie ervan op uploaders die zijn opgenomen in uw modellen. Gebruik de volgende opdracht om een ​​uploader te genereren:

rails genereren uploader afbeelding

Nu, binnen app / uploaders, je zult een nieuw bestand vinden met de naam image_uploader.rb. Merk op dat het enkele nuttige opmerkingen en voorbeelden bevat, dus u kunt het gebruiken om aan de slag te gaan. In deze demo gebruiken we ActiveRecord, maar Carrierwave heeft ook ondersteuning voor Mongoid, Sequel en DataMapper.

Vervolgens moeten we of opnemen berg deze uploader in het model:

modellen / post.rb

mount_uploader: image, ImageUploader

De uploader heeft al goede standaardinstellingen, maar we moeten op zijn minst kiezen waar de geüploade bestanden worden opgeslagen. Laten we voorlopig bestandsopslag gebruiken:

uploaders / image_uploader.rb

opslag: bestand

Standaard worden bestanden geplaatst binnen de public / uploads directory, dus het is het beste om het uit te sluiten van het versiecontrolesysteem:

.gitignore

public / uploads

U kunt ook het store_dir methode in je uploader om een ​​andere locatie te kiezen.

Op dit punt kunnen we een nieuwe weergave en gedeeltelijke partitie maken om te beginnen met het uploaden van bestanden:

views / berichten / new.html.erb

Voeg bericht toe

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

views / berichten / _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 %>

Merk op dat de PostsController hoeft niet te worden aangepast omdat we het al hebben toegestaan beeld attribuut.

Maak ten slotte de bewerkingsweergave:

views / berichten / edit.html.erb

Bericht bewerken

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

Dat is het! U kunt de server opstarten en proberen een bericht met een afbeelding te maken. Het probleem is dat deze afbeelding nergens zichtbaar is, dus laten we doorgaan naar de volgende sectie en een showpagina toevoegen!

Afbeeldingen weergeven

Dus de enige weergave die we nog niet hebben gemaakt, is laten zien. Nu toevoegen:

views / berichten / 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) %>

Zoals u kunt zien, is het weergeven van een bijlage heel eenvoudig: u hoeft alleen maar te zeggen wat u moet doen @ post.image.url om de URL van een afbeelding te pakken. Gebruik de. Om een ​​pad naar het bestand te krijgen huidige pad methode. Merk op dat Carrierwave ook een beeld? methode om te controleren of er al dan niet een bijlage aanwezig is (de beeld methode zelf zal nooit terugkeren nul, zelfs als het bestand niet aanwezig is).

Nu moet je na het navigeren naar een bericht een afbeelding zien, maar deze kan te groot lijken: we beperken de dimensies immers nergens. Natuurlijk hadden we de afbeelding kunnen verkleinen met enkele CSS-regels, maar het is veel beter om een ​​miniatuur te genereren nadat het bestand is geüpload. Dit vereist echter enkele extra stappen.

Miniaturen genereren

Om afbeeldingen bij te snijden en te schalen, hebben we een aparte tool nodig. Out-of-the-box Carrierwave heeft ondersteuning voor RMagick- en MiniMagick-edelstenen die op hun beurt worden gebruikt om afbeeldingen te manipuleren met behulp van ImageMagick. ImageMagick is een open-sourceoplossing waarmee u bestaande afbeeldingen kunt bewerken en nieuwe kunt genereren. Voordat u verder gaat, moet u het downloaden en installeren. Vervolgens bent u vrij om een ​​van de twee edelstenen te kiezen. Ik blijf bij MiniMagick, omdat het veel eenvoudiger te installeren is en het betere ondersteuning biedt: 

Gemfile

gem 'mini_magick'

Rennen:

bundel installeren

Voeg vervolgens MiniMagick toe aan uw uploader:

uploaders / image_uploader.rb

omvatten CarrierWave :: MiniMagick

Nu moeten we gewoon een nieuwe versie introduceren in onze uploader. Het concept van versies (of stijlen) wordt gebruikt in veel bibliotheken voor het uploaden van bestanden; het betekent gewoon dat er extra bestanden op basis van de originele bijlage worden gemaakt met bijvoorbeeld verschillende dimensies of indelingen. Introduceer een nieuwe versie genaamd duim:

uploaders / image_uploader.rb

versie: thumb do process resize_to_fill: [350, 350] end

U kunt zoveel versies hebben als u wilt en bovendien kunnen versies zelfs op andere worden gebouwd:

uploaders / image_uploader.rb

versie: small_thumb, from_version:: thumb do process resize_to_fill: [20, 20] einde

Als u al enkele afbeeldingen hebt geüpload, hebben deze geen miniaturen beschikbaar. Dit is echter geen probleem, omdat u ze opnieuw kunt maken vanuit de Rails-console:

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

Tenslotte, toon je thumbnail met een link naar de originele afbeelding:

views / berichten / show.html.erb

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

Start de server op en observeer het resultaat!

Validaties toevoegen

Momenteel werkt ons uploaden, maar we valideren de gebruikersinvoer helemaal niet, wat natuurlijk slecht is. Zolang we alleen met afbeeldingen willen werken, laten we de .png, .jpg en .gif extensies op de witte lijst plaatsen:

uploaders / image_uploader.rb

def extensie_whitelist% w (jpg jpeg gif png) einde

U kunt ook inhoudstypecontroles toevoegen door a te definiëren content_type_whitelist methode:

uploaders / image_uploader.rb

def content_type_whitelist / image \ // end

Als alternatief is het mogelijk om sommige bestandstypen op een zwarte lijst te zetten, bijvoorbeeld uitvoerbare bestanden, door de content_type_blacklist methode.

Los van het controleren van het type en de extensie van een bestand, laten we het afdwingen om minder dan 1 megabyte te zijn. Om dit te doen, hebben we een extra gem ondersteunende bestandsvalidaties nodig voor ActiveModel:

Gemfile

gem 'file_validators'

Installeer het:

bundel installeren

Voer nu de gewenste validaties in (merk op dat ik ook controles toevoeg voor de titel en lichaam attributen):

modellen / post.rb

validates: title, presence: true, length: minimum: 2 validates: body, presence: true validates: image, file_size: less_than: 1.megabytes

Het volgende dat u moet doen, is om I18n-vertalingen toe te voegen voor de foutmeldingen van Carrierwave:

config / locales / en.yml

nl: errors: messages: carrierwave_processing_error: "Afbeelding kan niet worden verkleind." carrierwave_integrity_error: "Geen afbeelding." carrierwave_download_error: "Kon afbeelding niet downloaden." extension_whitelist_error: "U mag geen% extension -bestanden uploaden, toegestane typen:% allowed_types" extension_blacklist_error: "U mag geen% extension -bestanden uploaden, verboden typen:% prohibited_types"

Momenteel geven we nergens validatiefouten weer, dus laten we een gedeelde partitie maken:

views / shared / _errors.html.erb

<% if object.errors.any? %> 

Sommige fouten zijn gevonden:

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

Gebruik dit deel in het formulier:

views / berichten / _form.html.erb

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

Probeer nu enkele ongeldige bestanden te uploaden en observeer het resultaat. Het zou moeten werken, maar als u een geldig bestand kiest en de titel of body niet invult, dan mislukken de controles en wordt er een fout weergegeven. Het bestandsveld wordt echter leeggemaakt en de gebruiker moet de afbeelding opnieuw kiezen, wat niet erg handig is. Om dit te verhelpen, moeten we een nieuw veld toevoegen aan het formulier.

Bestaande bestanden over verzoeken heen

Bestaande bestanden via nieuwe redisplays is eigenlijk vrij eenvoudig. Het enige dat u hoeft te doen is een nieuw verborgen veld toevoegen en dit in de controller toestaan:

views / shared / _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 de image_cache wordt automatisch ingevuld en de afbeelding zal niet verloren gaan. Het kan handig zijn om ook een miniatuur weer te geven, zodat de gebruiker begrijpt dat de afbeelding is verwerkt: 

views / shared / _form.html.erb

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

Afbeeldingen verwijderen

Een andere veel voorkomende functie is de mogelijkheid om bijgevoegde bestanden te verwijderen bij het bewerken van een record. Met Carrierwave is het implementeren van deze functie geen probleem. Voeg een nieuw selectievakje toe aan het formulier:

views / shared / _form.html.erb

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

En laat het toe remove_image attribuut:

posts_controller.rb

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

Dat is het! Als u een afbeelding handmatig wilt verwijderen, gebruikt u de remove_image! methode:

@ post.remove_image!

Uploaden vanaf een externe locatie

Carrierwave biedt ook een zeer gave functie uit de doos: de mogelijkheid om bestanden van externe locaties te uploaden via hun URL. Laten we deze mogelijkheid nu introduceren door een nieuw veld toe te voegen en het bijbehorende kenmerk toe te staan: 

views / shared / _form.html.erb

<%= f.text_field :remote_image_url %> Voer de URL voor een afbeelding in

posts_controller.rb

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

Hoe cool is dat? U hoeft helemaal geen wijzigingen aan te brengen en u kunt deze functie meteen testen!

Werken met meerdere uploads

Stel dat we willen dat onze post meerdere bijlagen beschikbaar heeft. Met de huidige opzet is het niet mogelijk, maar gelukkig ondersteunt Carrierwave zo'n scenario ook. Om deze functie te implementeren, moet u een veld met een geserialiseerd veld (voor SQLite) of een JSON-veld (voor Postgres of MySQL) toevoegen. Ik geef de voorkeur aan de laatste optie, dus laten we nu overgaan naar een nieuwe database-adapter. Verwijder de sqlite3-edelsteen van de Gemfile en voeg in plaats daarvan pg toe:

Gemfile

edelsteen 'pg'

Installeer het:

bundel installeren

Wijzig de databaseconfiguratie als volgt:

config / database.yml

standaard: & standaard adapter: postgresql pool: 5 time-out: 5000 ontwikkeling: <<: *default database: upload_carrier_dev username: 'YOUR_USER' password: 'YOUR_PASSWORD' host: localhost

Maak de bijbehorende Postgres-database en genereer en pas de migratie toe:

rails g migratie bijlagen add_attachments_to_posts: json rails db: migrate

Als u liever bij SQLite blijft, volgt u de instructies in de documentatie van Carrierwave.

Koppel nu de uploaders (let op de meervoudsvorm!):

model / post.rb

mount_uploaders: bijlagen, ImageUploader

Ik gebruik dezelfde uploader voor bijlagen, maar u kunt natuurlijk een nieuwe genereren met een andere configuratie.

Voeg het veld met meerdere bestanden toe aan uw formulier:

views / shared / _form.html.erb

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

Zolang de bijlagen veld een array bevat, moet dit op de volgende manier worden toegestaan:

posts_controller.rb

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

Ten slotte kunt u de bijlagen van de bijlage herhalen en deze zoals gebruikelijk weergeven:

views / shared / 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 op dat elke bijlage een thumbnail zal hebben zoals deze is geconfigureerd in onze ImageUploader. Leuk!

Cloud-opslag gebruiken

Vasthouden aan bestandsopslag is niet altijd handig en / of mogelijk, omdat het op Heroku bijvoorbeeld niet mogelijk is om aangepaste bestanden op te slaan. Daarom kun je je afvragen hoe je Carrierwave met Amazon S3 cloudopslag kunt trouwen? Wel, dat is ook een vrij gemakkelijke taak. Carrierwave is afhankelijk van de mist-aws edelsteen om deze functie te implementeren:

Gemfile

gem "mist-aws"

Installeer het:

bundel installeren

Laten we een initializer voor Carrierwave maken en de cloudopslag wereldwijd configureren:

config / initialiseerders / carrierwave.rb

CarrierWave.configuratie do | config | config.fog_provider = 'fog / aws' config.fog_credentials = provider: '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'] einde

Er zijn nog enkele andere opties beschikbaar, die u kunt vinden in de documentatie.

Ik gebruik het juweel dotenv-rails om de omgevingsvariabelen op een veilige manier in te stellen, maar u kunt een andere optie kiezen. Zorg er echter voor dat uw S3-sleutelpaar niet openbaar beschikbaar is, omdat anders iedereen iets naar uw bucket kan uploaden!

Vervang vervolgens de opslag: bestand regel met:

uploaders / image_uploader.rb

opslag: mist

Afgezien van S3 ondersteunt Carrierwave uploads naar Google Storage en Rackspace. Deze services zijn ook eenvoudig in te stellen.

Conclusie

Dit is het voor vandaag! We hebben alle belangrijke functies van Carrierwave besproken en nu kunt u het in uw projecten gaan gebruiken. Het heeft enkele extra opties beschikbaar, dus bekijk de documentatie.

Als je vastzit, aarzel dan niet om je vragen te stellen. Het kan ook handig zijn om een ​​kijkje te nemen in de wiki van Carrierwave, waar nuttige "how to" -artikelen worden gehost die veel veelgestelde vragen beantwoorden.

Daarom bedank ik je dat je bij me bent gebleven en dat je blij bent met het coderen!