Överföring av filer med skenor och helgedom

Det finns många filer som laddas upp där som CarrierWave, Paperclip och Dragonfly, för att nämna några. De har alla sina detaljer, och förmodligen har du redan använt minst en av dessa pärlor.

Idag vill jag dock introducera en relativt ny men väldigt cool lösning som heter Shrine, skapad av Janko Marohnić. I motsats till några andra liknande pärlor har den ett modulärt tillvägagångssätt, vilket betyder att varje funktion är packad som en modul (eller plugin i helgedomens terminologi). Vill du stödja valideringar? Lägg till ett plugin. Vill du göra någon filbehandling? Lägg till ett plugin! Jag älskar verkligen detta tillvägagångssätt, eftersom det gör att du enkelt kan styra vilka funktioner som finns tillgängliga för vilken modell.

I den här artikeln ska jag visa dig hur man ska:

  • Integrera Shrine i en Rails applikation
  • konfigurera det (globalt och per uppladdare)
  • lägg till möjligheten att ladda upp filer
  • bearbeta filer
  • lägg till valideringsregler
  • lagra ytterligare metadata och använd filmolnagring med Amazon S3

Källkoden för den här artikeln finns på GitHub.

Den fungerande demo finns här.

Integrerande helgedom

För att starta, skapa en ny Rails-applikation utan standard testpaket:

skenar nytt FileGuru -T

Jag kommer att använda Rails 5 för denna demonstration, men de flesta koncepten gäller även versioner 3 och 4.

Släpp Shrine pärla i din Gemfile:

pärla "helgedom"

Kör sedan:

buntinstallation

Nu behöver vi en modell som jag ska ringa Foto. Shrine lagrar all filrelaterad information i en särskild textkolumn som slutar med a _data ändelse. Skapa och tillämpa motsvarande migrering:

skenor g modell Foto titel: sträng image_data: textrader db: migrera

Observera att för äldre versioner av Rails ska det senare kommandot vara:

rake db: migrera

Konfigurationsalternativ för helgedom kan ställas både globalt och per modell. Globala inställningar görs förstås, inuti initialiseringsfilen. Där ska jag ansluta de nödvändiga filerna och plugin-program. Plugins används i Shrine för att extrahera funktionalitet i separata moduler, vilket ger dig fullständig kontroll över alla tillgängliga funktioner. Till exempel finns plugins för validering, bildbehandling, caching-bilagor och mer.

För närvarande lägger vi till två plugins: en för att stödja ActiveRecord och en annan för att ställa in loggning. De kommer att ingå globalt. Ställ också upp filsystemlagring:

config / initializers / shrine.rb

kräva "shrine" kräver "shrine / storage / file_system" Shrine.plugin: activiverecord Shrine.plugin: loggar, logger: Rails.logger Shrine.storages = cache: Shrine :: Storage :: FileSystem.new ("public", prefix : "uploads / cache"), butik: Shrine :: Lagring :: FileSystem.new ("public", prefix: "uploads / store"),

Logger kommer helt enkelt att mata in några felsökningsinformation i konsolen för att du säger hur mycket tid som spenderades för att bearbeta en fil. Detta kan komma till nytta.

2015-10-09T20: 06: 06.676Z # 25602: STORE [cache] ImageUploader [: avatar] Användare [29543] 1 fil (0.1s) 2015-10-09T20: 06: 06.854Z # 25602: PROCESS [store]: ImageUploader [: avatar] Användare [29543] 1-3 filer (0.22s) 2015-10-09T20: 06: 07.133Z # 25602: DELETE [förstört]: ImageUploader [: avatar] Användare [29543] 3 filer (0.07s)

Alla uppladdade filer lagras inuti offentliga / uppladdningar katalogen. Jag vill inte spåra dessa filer i Git, så uteslut denna mapp:

.gitignore

offentliga / uppladdningar

Skapa nu en särskild "uppladdare" -klass som ska vara värd för modellspecifika inställningar. För tillfället kommer den här klassen att vara tom:

modeller / image_uploader.rb

klass ImageUploader < Shrine end

Slutligen, inkludera denna klass inuti Foto modell:

modeller / photo.rb

inkludera ImageUploader [: image]

[:bild] lägger till ett virtuellt attribut som ska användas vid konstruktion av en blankett. Ovanstående rad kan skrivas om som:

 inkludera ImageUploader.attachment (: bild) # eller inkludera ImageUploader :: Attachment.new (: image) 

Trevlig! Nu är modellen utrustad med Shrine-funktionalitet, och vi kan gå vidare till nästa steg.

Controller, Views och Routes

För den här demonstrationen behöver vi bara en kontroller för att hantera foton. De index sidan kommer att fungera som root:

pages_controller.rb

klass PhotosController < ApplicationController def index @photos = Photo.all end end

Vyn:

visningar / foton / index.html.erb

foton

<%= link_to 'Add Photo', new_photo_path %> <%= render @photos %>

För att göra @photos array, en del är obligatorisk:

visningar / foton / _photo.html.erb

<% if photo.image_data? %> <%= image_tag photo.image_url %> <% end %>

<%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>

image_data? är en metod som presenteras av ActiveRecord som kontrollerar om en post har en bild.

bild URL är en helgedomsmetod som helt enkelt återger en väg till originalbilden. Det är självklart mycket bättre att visa en liten miniatyr istället, men vi tar hand om det senare.

Lägg till alla nödvändiga vägar:

config / routes.rb

 resurser: bilder, bara: [: ny,: skapa,: index,: redigera,: uppdatera] root 'foton # index'

Det här är det - grundarbetet är klart, och vi kan gå vidare till den intressanta delen!

Överför filer

I det här avsnittet visar jag hur du lägger till funktionaliteten för att faktiskt ladda upp filer. Kontrolleråtgärderna är väldigt enkla:

photos_controller.rb

def new @photo = Photo.new end def skapa @photo = Photo.new (photo_params) om @ photo.save flash [: success] = 'Foto lagt till!' redirect_to photos_path else render "new" slutet

Den enda gotchaen är den för starka parametrar som du måste tillåta bild virtuell attribut, inte den image_data.

photos_controller.rb

privat def photo_params params.require (: foto) .permit (: titel,: bild) slutet

Skapa ny se:

visningar / foton / new.html.erb

Lägg till foto

<%= render 'form' %>

Formulärens partiella är också trivial:

visningar / foton / _form.html.erb

<%= form_for @photo do |f| %> <%= render "shared/errors", object: @photo %> <%= f.label :title %> <%= f.text_field :title %> <%= f.label :image %> <%= f.file_field :image %> <%= f.submit %> <% end %>

Återigen, notera att vi använder bild attribut, inte den image_data.

Slutligen lägg till en annan partiell för att visa fel:

visningar / delad / _errors.html.erb

<% if object.errors.any? %> 

Följande fel hittades:

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

Det här är ganska mycket allt-du kan börja ladda upp bilder just nu.

valideringar

Naturligtvis måste mycket mer arbete göras för att slutföra demo-appen. Det största problemet är att användarna kan ladda upp absolut vilken typ av fil som helst, vilket inte är särskilt bra. Lägg därför till ytterligare ett plugin för att stödja valideringar:

config / inititalizers / shrine.rb

Shrine.plugin: validation_helpers

Ställ in valideringslogiken för ImageUploader:

modeller / image_uploader.rb

Attacher.validate validate_max_size 1.megabyte, meddelande: "är för stor (max är 1 MB)" validate_mime_type_inclusion ['image / jpg', 'image / jpeg', 'bild / png'] slut

Jag tillåter bara JPG och PNG bilder mindre än 1 MB att ladda upp. Tweak dessa regler som du tycker är lämplig.

MIME-typer

En annan viktig sak att notera är att, som vanligt, ska Shrine bestämma en fils MIME-typ med HTTP-rubriken Content-Type. Den här rubriken skickas av webbläsaren och ställs endast ut baserat på filens tillägg, vilket inte alltid är önskvärt.

Om du vill bestämma MIME-typen baserat på filens innehåll, använd sedan ett plugin som heter determ_mime_type. Jag kommer att inkludera den inom uppladdarklassen, eftersom andra modeller kanske inte kräver denna funktion:

modeller / image_uploader.rb

plugin: determine_mime_type

Detta plugin kommer som standard att använda Linux-filverktyget.

Caching bifogade bilder

För närvarande, när en användare skickar en blankett med felaktiga uppgifter, visas formuläret igen med fel som gjorts ovan. Problemet är emellertid att den bifogade bilden kommer att gå vilse, och användaren måste välja den en gång till. Det här är väldigt enkelt att fixa med ännu ett annat plugin som heter cached_attachment_data:

modeller / image_uploader.rb

plugin: cached_attachment_data

Lägg nu enkelt ett doldt fält i din formulär.

visningar / foton / _form.html.erb

<%= f.hidden_field :image, value: @photo.cached_image_data %> <%= f.label :image %> <%= f.file_field :image %>

Redigering av ett foto

Nu kan bilder laddas upp, men det finns inget sätt att redigera dem, så låt oss fixa det direkt. Den motsvarande styrelsens åtgärder är något triviala:

photos_controller.rb

def redigera @photo = Photo.find (params [: id]) slutdefinition @photo = Photo.find (params [: id]) om @ photo.update_attributes (photo_params) flash [: success] = 'Foto redigerade!' redirect_to photos_path else render redigera änden

Det samma _form delvis kommer att utnyttjas:

visningar / foton / edit.html.erb

Redigera Foto

<%= render 'form' %>

Trevligt men inte tillräckligt: ​​Användare kan fortfarande inte ta bort en uppladdad bild. För att tillåta detta behöver vi-gissa vad-ett annat plugin: 

modeller / image_uploader.rb

plugin: remove_attachment

Det använder ett virtuellt attribut som heter : remove_image, så tillåta det inom kontrollenheten:

photos_controller.rb

def photo_params params.require (: photo) .permit (: title,: image,: remove_image) avsluta

Visa bara en kryssruta för att ta bort en bild om en post har en bilaga på plats:

visningar / foton / _form.html.erb

<% if @photo.image_data? %> Ta bort bilagan: <%= f.check_box :remove_image %> <% end %>

Generera en miniatyrbild

För närvarande visar vi ursprungliga bilder, vilket inte är det bästa sättet att förhandsgranska: bilder kan vara stora och uppta för mycket utrymme. Självklart kan du helt enkelt använda CSS bredd och höjd attribut, men det är också en dålig idé. Du ser att även om bilden är liten för att använda format, behöver användaren fortfarande ladda ner originalfilen, vilket kan vara ganska stor.

Därför är det mycket bättre att generera en liten förhandsgranskningsbild på serverns sida under den första uppladdningen. Detta innebär två plugins och två extra pärlor. För det första släppa i pärlorna:

pärla "image_processing" pärla "mini_magick", "> = 4.3.5"

Image_processing är en speciell pärla skapad av författaren av Shrine. Den presenterar några metoder på hög nivå för att manipulera bilder. Denna pärla är beroende av mini_magick, en Ruby wrapper för ImageMagick. Som du har gissat behöver du ImageMagick på ditt system för att kunna köra den här demo.

Installera dessa nya pärlor:

buntinstallation

Nu innehåller plugins tillsammans med deras beroende:

modeller / image_uploader.rb

kräva "image_processing / mini_magick" class ImageUploader < Shrine include ImageProcessing::MiniMagick plugin :processing plugin :versions # other code… end

Bearbetning är plugin som tillåter oss att manipulera en bild (till exempel krympa, rotera, konvertera till ett annat format, etc.). Versioner kan i sin tur ge oss en bild i olika varianter. För denna demo lagras två versioner: "original" och "tumme" (omformat till 300x300).

Här är koden för att bearbeta en bild och lagra sina två versioner:

modeller / image_uploader.rb

klass ImageUploader < Shrine process(:store) do |io, context|  original: io, thumb: resize_to_limit!(io.download, 300, 300)  end end

resize_to_limit! är en metod som tillhandahålls av image_processing-pärlan. Den krymper bara en bild ner till 300x300 om det är större och gör ingenting om det är mindre. Dessutom håller den det ursprungliga bildförhållandet.

Nu när du visar bilden behöver du bara ge antingen :original eller :tumme argument till bild URL metod:

visningar / foton / _photo.html.erb

<% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %> <% end %>

<%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>

Samma sak kan göras inuti formuläret:

visningar / foton / _form.html.erb

<% if @photo.image_data? %> <%= image_tag @photo.image_url(:thumb) %> Ta bort bilagan: <%= f.check_box :remove_image %> <% end %>

För att automatiskt radera de bearbetade filerna efter att uppladdning är klar kan du lägga till ett plugin som heter delete_raw:

modeller / image_uploader.rb

plugin: delete_raw

Bildens metadata

Bortsett från att du faktiskt gör en bild kan du också hämta dess metadata. Låt oss till exempel visa originalfotoets storlek och MIME-typ:

visningar / foton / _photo.html.erb

<% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %>

Storlek <%= photo.image[:original].size %> bitgrupper
MIME-typ <%= photo.image[:original].mime_type %>

<% end %>

<%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>

Vad sägs om dess dimensioner? Tyvärr lagras de inte som standard, men det här är möjligt med ett plugin som heter store_dimensions.

Bildens mått

Plugin store_dimensions beror på snabbbilden, så koppla upp den nu:

pärla "snabbbild"

Glöm inte att springa:

buntinstallation

Nu bara inkludera plugin:

modeller / image_uploader.rb

plugin: store_dimensions

Och visa måtten med hjälp av bredd och höjd metoder:

visningar / foton / _photo.html.erb

<% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %>

Storlek <%= photo.image[:original].size %> bitgrupper
MIME-typ <%= photo.image[:original].mime_type %>
Mått <%= "#photo.image[:original].widthx#photo.image[:original].height" %>

<% end %>

<%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>

Det finns också en mått tillgänglig metod som returnerar en array som innehåller bredd och höjd (till exempel, [500, 750]).

Flytta till molnet

Utvecklare väljer ofta molntjänster för att vara värd för uppladdade filer, och Shrine presenterar en sådan möjlighet. I det här avsnittet visar jag hur du laddar upp filer till Amazon S3.

Som det första steget, ta med två fler pärlor i Gemfile:

pärla "aws-sdk", "~> 2.1" -grupp: utveckling gör pärla "dotenv-rails" slutet

aws-sdk krävs för att arbeta med S3: s SDK, medan dotenv-skenor kommer att användas för att hantera miljövariabler i utveckling.

buntinstallation

Innan du fortsätter bör du få ett nyckelpar för åtkomst till S3 via API. För att få det, logga in (eller anmäl dig) till Amazon Web Services Console och navigera till Säkerhetsuppgifter> Användare. Skapa en användare med behörigheter för att manipulera filer på S3. Här är den enkla policyen som presenterar full tillgång till S3:

"Version": "2016-11-14", "Uttalande": ["Effekt": "Tillåt", "Åtgärd": "s3: *", "Resurs": "*"]

Ladda ner den skapade användarens nyckelpar. Alternativt kan du använda root accessnycklar, men jag avskräcka starkt du gör det eftersom det är mycket osäkert.

Skapa sedan en S3-hink som värd för dina filer och lägg till en fil i projektets rot för att vara värd för din konfiguration:

.env

S3_KEY = YOUR_KEY S3_SECRET = YOUR_SECRET S3_BUCKET = YOUR_BUCKET S3_REGION = DIN_REGION

Utsätt aldrig någonsin den här filen till allmänheten, och se till att du utesluter den från Git:

.gitignore

.env

Ändra nu Shrine's globala konfiguration och introducera ett nytt lagringsutrymme:

config / initializers / shrine.rb

kräver "shrine" kräver "shrine / storage / s3" s3_options = access_key_id: ENV ['S3_KEY'], secret_access_key: ENV ['S3_SECRET'], region: ENV ['S3_REGION'], hink: ENV ['S3_BUCKET'] , Shrine.storages = cache: Shrine :: Lagring :: FileSystem.new ("public", prefix: "uploads / cache"), butik: Shrine :: Lagring :: S3.new (prefix: "store" ** s3_options),

Det är allt! Inga ändringar måste göras till andra delar av appen, och du kan omedelbart testa det här nya lagret. Om du får fel från S3 relaterade till felaktiga nycklar, se till att du kopierat nyckeln och hemligheten noggrant, utan några efterföljande utrymmen och osynliga speciella symboler.

Slutsats

Vi har kommit till slutet av den här artikeln. Förhoppningsvis, nu känner du dig mycket säker på att använda helgedom och är angelägna om att använda den i ett av dina projekt. Vi har diskuterat många av denna pärls funktioner, men det finns ännu mer, som möjligheten att lagra ytterligare sammanhang tillsammans med filer och direkt uppladdningsmekanism. 

Därför, bläddra i Shrine dokumentation och dess officiella hemsida, som grundligt beskriver alla tillgängliga plugins. Om du har andra frågor kvar om denna pärla, tveka inte att skicka in dem. Jag tackar dig för att du bodde hos mig, och vi ses snart!