Awatary Użytkowników - Wysyłanie Obrazków z Paperclip

Przez:, Z dnia:

Ten tutorial zakłada, że już ukończyłeś:

  1. Styling Devise forms with TwitterBootstrap
  2. Adding custom fields to Devise

Stworzyliśmy już naszych użytkowników. Nadszedł czas na nadanie im możliwości załadowania ich własnych awatarów i obrazkówm, którymi chcą się podzielić.

Ponieważ pracujemy z Heroku.com jako platformą, do której robimy deploy, będziemy mieli trochę więcej kroków do wykonania (następny tutorial).

Będziemy podążać tą ścieżką:

  1. Dodaj gem Paperclip
  2. Dodaj "avatar" do modelu użytkownika
  3. Dodaj wysyłanie obrazka przez formularze Devise

Zaczynajmy.

Krok 1: Dodaj gem Paperclip

Są dwa sposoby na działanie z obrazkami w aplikacjach Rails. Niektórzy uzywają Paperclip, inni Carrierwave. Będziemy używać Paperclip. Zakładam także, że masz zrobionych użytkowników z Devise.

Najpierw odwiedź i zobacz dokumentację Paperclip, będziemy używać trochę tam przedstawionych instrukcji. Możesz zobaczyć też dodatkowe ustawienia gema.

Zaczniemy od zainstalowania procesora obrazów - ImageMagic, który będzie odpowidzialny za manipulowanie naszymi obrazami, zmienianie rozmiaru, przycinanie, itd. Otwórz terminal i, jeżeli posiadasz Mac, wpisz:

bash

$ brew install imagemagick

Jeżeli uzywasz linuksa, wpisz:

bash

$ sudo-apt-get install imagemagick

Krok 1.2

Teraz musimy dodać Paperclip do Gemfile.

Gemfile

source 'https://rubygems.org'

# existing gems

gem 'paperclip', '~> 4.1'

Krok 1.3

I uruchomić bundler:

bash

$ bundle install

Krok 2: Dodaj "awatar" do Modelu Uzytkownika

Zacznę z małym objaśnieniem. Kiedy wysyłamy obrazki, nie zapisujemy ich w bazie danych. Nasza baza danych nie może przetrzymywać żadnych plików. Więc to co będziemy robić, albo Paperclip będzie robił za nas, to zapisywanie obrazków do wyznaczonego folderu. Jak więc możemy później znaleźć gdzie się znajduje konkretny awatar? Dla takich rzeczy usimy dodać miejsca w bazie danych, nazwę i rozmiar pliku. Na stronie Github możesz znaleźć komendę do automatycznego wygenerowania migracji, ale mieliśmy z nią ostatnio problemy, dlatego też stworzymy migrację od ręki. Wpisz w termianlu:

bash

$ rails generate migration add_avatars_to_users

      invoke  active_record
      create    db/migrate/20140516064357_add_avatars_to_users.rb

Krok 2.2

Otwórz teraz plik migracyjny i wklej kod, który doda pola awatarów do modelu użytkownika:

db/migrate/20140516064357_add_avatars_to_users.rb

class AddAvatarsToUsers < ActiveRecord::Migration
  def self.up
    change_table :users do |t|
      t.attachment :avatar
    end
  end

  def self.down
    drop_attached_file :users, :avatar
  end
end

Krok 2.3

Zrób migrację bazy danych:

bash

$ rake db:migrate

Krok 2.4

Pozostało nam jeszcze dodać deklarację w pliku modelu użytkownika by powidomić go, że posiada dołączony plik.

class User < ActiveRecord::Base
  # existing code

  has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100#" }, :default_url => "/images/:style/missing.png"
  validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
end

Deklaracja składa się z kilku rzeczy. Pierwszą z nich jest oznajmienie, że istnieje plik dołączony do modelu użytkownika o nazwie "avatar". Możemy go nazwać inaczej, powiedzmy "picture", oczywiście wtedy musielibyśmy zmienić powyższy kod.

Dalej określamy style awatarów: medium i thumb. Możemy dodać więcej lub mniej stylów. Zależy od doświadczeń użytkownika. Chcemy wysyłać małe pliki żeby wszystko działało szybko, ale czasem potrzebujemy dobrej jakości obrazów. Jeżeli zamieżasz mieć bardzo duże obrazki musiałbyś rozważyć zmianę rozmiarów. "300x300>" oznacza, że obraz jest większy niż 300px na 300px, proporcjonalnie zmniejszymy skalę tak, że rozmiar nie będzie przekraczał 300 pikseli. Jeżeli chcielibyśmy przyciąć nasze obrazki tak, by były kwadratowe - napisalibyśmy wtedy "300x300#".

Ostatnią rzeczą jest domyślny adres. Jest to obraz, który będzie wyświetlony jeżli awatar nie będzie obecny. Może jeszcze użytkownik nie dodał, może wystąpił błąd podczas wysyłania. Musimy się upewnić, że zastępczy obrazek istnieje w podanej przez nas lokalizacji. Domyślnie Rails będzie szukał w folderze "public". Jeżeli więc wersja "thumb" nie istnieje - wyśiwtlimy plik z public/images/thumb/missing.png. Musimy stworzyć folder "images". Wewnątrz tego folderu potrzebujemy dodać dwa foldery: "thumb" i "medium". Obydwa powinny zawierać plik o nazwie "missing.png". W folderze thumb jego rozmiar powinien wynosić 100 na 100 pikseli, a w medium 300 na 300 pikseli.

success after uploading avatar

Dodaliśmy także walidacje. Bez tej linijki wysyłanie obrazków by nie działało. Walidacja ta jest cechą bezwieczeństwa zapobiegającą przed wysyłaniem złośliwych plików.

Świetnie, nasza strona może przechowywać obrazki, ale wciąż nie mamy sposobu na wysyłanie ich przez formularze z interfejsu użytkownika.

Krok 2.5

Ponieważ do zarządzania użytkownikami używamy Devise będziemy musieli się upewnić, że Devise pozwala na nowe awatary. Jeżeli jeszcze nie widziałeś, rzuć okiem na tutorial o dodawaniu własnych pól do Devise.

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
    # Prevent CSRF attacks by raising an exception.
    # For APIs, you may want to use :null_session instead.
    protect_from_forgery with: :exception
    before_filter :configure_permitted_parameters, if: :devise_controller?

    protected

    def configure_permitted_parameters
        devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password) }
        devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:name, :email, :password, :current_password, :is_female, :date_of_birth, :avatar) }
    end
end

Krok 3: Dodaj wysyłanie obrazka przez formularze Devise

Ostatnim krokiem do sprawienia, że wysyłanie plików będzie działać jest dodanie pól obrazków do formularza edycji:

app/views/users/registrations/edit.html.erb

<h2>Edit <%= resource_name.to_s.humanize %></h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'form-horizontal' }) do |f| %>
    <%= devise_error_messages! %>

    <div class="form-group">
        <%= f.label :avatar, class: 'col-sm-2 control-label'  %>
        <div class="col-sm-6">
            <%= f.file_field :avatar %>
        </div>
    </div>


    <div class="form-group">
        <%= f.label :name, class: 'col-sm-2 control-label'  %>
        <div class="col-sm-6">
            <%= f.text_field :name, autofocus: true, class: 'form-control'  %>
        </div>
    </div>

    <div class="form-group">
        <%= f.label :is_female, "Gender", class: 'col-sm-2 control-label'  %>
        <div class="col-sm-6">
            <%= f.radio_button :is_female, true %> <%= f.label :is_female, "Female" %>
            <%= f.radio_button :is_female, false %> <%= f.label :is_female, "Male" %>
        </div>
    </div>


    <div class="form-group">
        <%= f.label :date_of_birth, class: 'col-sm-2 control-label'  %>
        <div class="col-sm-6">
            <%= f.date_select :date_of_birth, start_year: 1920, end_year: 2000, class: 'form-control'  %>
        </div>
    </div>

    <div class="form-group">
        <%= f.label :email, class: 'col-sm-2 control-label'  %>
        <div class="col-sm-6">
            <%= f.email_field :email, class: 'form-control'  %>
        </div>
    </div>

    <div class="form-group">
        <%= f.label :password, class: 'col-sm-2 control-label'  %> <i>(leave blank if you don't want to change it)</i>
        <div class="col-sm-6">
            <%= f.password_field :password, autocomplete: "off", class: 'form-control'  %>
        </div>
    </div>


    <div class="form-group">
        <%= f.label :password_confirmation, class: 'col-sm-2 control-label' %>
        <div class="col-sm-6">
            <%= f.password_field :password_confirmation, autocomplete: "off", class: 'form-control'  %>
        </div>
    </div>

    <div class="form-group">
        <%= f.label :current_password, class: 'col-sm-2 control-label' %> <i>(we need your current password to confirm your changes)</i>
        <div class="col-sm-6">
            <%= f.password_field :current_password, autocomplete: "off", class: 'form-control' %>
        </div>
    </div>

    <div class="form-group">
        <div class="col-sm-offset-2 col-sm-6">
            <%= f.submit "Update", class: "btn btn-primary" %>
        </div>
    </div>

    <div class="form-group">
        <div class="col-sm-offset-2 col-sm-6">
            <%= render "users/shared/links" %>
        </div>
    </div>
<% end %>

<h5>Cancel my account</h5>

<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-default btn-xs' %></p>

<%= link_to "Back", :back %>

Edycja naszego profilu (http://localhost:3000/users/edit) powinna wyglądać tak:

form for uploading user's avatars

Po udanym załadowaniu pliku zobczysz powiadomienie:

success after uploading avatar

Krok 3.2

Niestety nigdzie nie wyświetlamy jeszcze awatarów użytkowników. Naprawmy to:

app/views/layouts/_menu.html.erb

<div class="collapse navbar-collapse" id="navbar-collapse-1">
    <ul class="nav navbar-nav navbar-right">
        <li><%= link_to 'Posts', '#' %></li>
        <% if current_user %>
            <li><%= link_to 'Edit Profile',edit_user_registration_path %></li>
            <li><%= link_to 'Logout', destroy_user_session_path, method: :delete %></li>

            <li class="round-image-50"><%= image_tag(current_user.avatar.url(:thumb)) %></li>

        <% else %>
            <li><%= link_to 'Login', new_user_session_path %></li>
        <% end %>
    </ul>
</div>

Krok 3.3

I dodajmy stylizację CSS:

app/assets/stylesheets/custom.css.scss

.round-image-50 {
    background-color: white;
    border: 1px solid #d9d9d9;
    border-radius: 25px;
    -moz-border-radius: 25px;
    -webkit-border-radius: 25px;
    height: 50px;
    width: 50px;
    overflow: hidden;
    text-align: center;
    img { width: 100% }
}

Teraz widzisz swój awatar w menu:

success after uploading avatar

Musisz się zalogować by móc oznaczyć tutorial jako ukończony żeby śledzić swój postęp



Komentarze

  • Z dnia: mohammad javad napisał:

    my image_tag(current_user...) does not work properly and it just shows missing picture?

  • Z dnia: mohammad javad napisał:

    i'm getting this error when uploading picture : Avatar has an extension that does not match its contents

  • Z dnia: Jose Pejuan napisał:

    This line right here: <%= imagetag(currentuser.avatar.url(:thumb)) %> its throwing me an error. Says the method doesn't exist

Dodaj komentarz

Możesz się zalogować by skomentować