"Uploading with Rails" श्रृंखला का यह एक और लेख है। आज हम CarrierWave से मिलेंगे—Rails के लिए सबसे लोकप्रिय फ़ाइल अपलोड समाधानों में से एक। मुझे CarrierWave पसंद है क्योंकि यह शुरू करना आसान है, इसमें बॉक्स से बाहर कई विशेषताएं हैं, और समुदाय के सदस्यों द्वारा लिखे गए दर्जनों "कैसे करें" लेख प्रदान करता है, इसलिए आप भटकेंगे नहीं।
इस लेख में आप सीखेंगे:
- CarrierWave को अपने Rails ऐप में एकीकृत करना
- वैलिडेशन जोड़ना
- रिक्वेस्ट के बीच फ़ाइलों को बनाए रखना
- फ़ाइलों को हटाना
- थंबनेल उत्पन्न करना
- रिमोट स्थानों से फ़ाइलें अपलोड करना
- एकाधिक फ़ाइल अपलोड पेश करना
- क्लाउड स्टोरेज के लिए समर्थन जोड़ना
इस लेख का स्रोत कोड GitHub पर उपलब्ध है। पढ़ने का आनंद लें!
नींव रखना
हमेशा की तरह, एक नया Rails एप्लिकेशन बनाकर शुरू करें:
rails new UploadingWithCarrierwave -T
इस डेमो के लिए मैं Rails 5.0.2 का उपयोग करूंगा। कृपया ध्यान दें कि CarrierWave 1 केवल Rails 4+ और Ruby 2 का समर्थन करता है। यदि आप अभी भी Rails 3 पर हैं, तो CarrierWave संस्करण 0.11 से जुड़ें।
CarrierWave को क्रिया में देखने के लिए, हम एक बहुत ही सरल ब्लॉगिंग एप्लिकेशन बनाएंगे जिसमें केवल एक Post मॉडल होगा। इसमें निम्न मुख्य विशेषताएं होंगी:
title(string)body(text)image(string)—यह फ़ील्ड पोस्ट से जुड़ी एक छवि (सटीक रूप से एक फ़ाइल का नाम) रखेगा
एक नया माइग्रेशन जनरेट करें और लागू करें:
rails g model Post title:string body:text image:string rails db:migrate
कुछ रूट सेट करें:
config/routes.rb
resources :posts root to: 'posts#index'
एक बहुत ही बुनियादी कंट्रोलर भी बनाएं:
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
अब index व्यू बनाते हैं:
views/posts/index.html.erb
<h1>Posts</h1> <%= link_to 'Add post', new_post_path %> <%= render @posts %>
और संबंधित पार्शियल:
views/posts/_post.html.erb
<h2><%= link_to post.title, post_path(post) %></h2> <p><%= truncate(post.body, length: 150) %></p> <p><%= link_to 'Edit', edit_post_path(post) %></p> <hr>
यहां मैं पोस्ट के पहले 150 प्रतीकों को प्रदर्शित करने के लिए Rails truncate विधि का उपयोग कर रहा हूं। अन्य व्यू और फ़ॉर्म पार्शियल बनाने से पहले, आइए पहले CarrierWave को एप्लिकेशन में एकीकृत करें।
CarrierWave को एकीकृत करना
Gemfile में एक नया जेम डालें:
Gemfile
gem 'carrierwave', '~> 1.0'
चलाएं:
bundle install
CarrierWave अपनी कॉन्फ़िगरेशन अपलोडर्स के अंदर संग्रहीत करता है जिन्हें आपके मॉडल में शामिल किया जाता है। एक अपलोडर जनरेट करने के लिए, निम्न कमांड का उपयोग करें:
rails generate uploader Image
अब, app/uploaders के अंदर, आपको image_uploader.rb नामक एक नई फ़ाइल मिलेगी। ध्यान दें कि इसमें कुछ उपयोगी टिप्पणियां और उदाहरण हैं, इसलिए आप इसका उपयोग शुरू करने के लिए कर सकते हैं। इस डेमो में हम ActiveRecord का उपयोग करेंगे, लेकिन CarrierWave Mongoid, Sequel, और DataMapper के लिए भी समर्थन रखता है।
अब, हमें इस अपलोडर को मॉडल में शामिल या माउंट करने की आवश्यकता है:
models/post.rb
mount_uploader :image, ImageUploader
अपलोडर में पहले से ही समझदार डिफ़ॉल्ट सेटिंग्स हैं, लेकिन कम से कम हमें यह चुनना होगा कि अपलोड की गई फ़ाइलें कहाँ संग्रहीत की जाएंगी। अभी के लिए, फ़ाइल स्टोरेज का उपयोग करते हैं:
uploaders/image_uploader.rb
storage :file
डिफ़ॉल्ट रूप से, फ़ाइलें public/uploads निर्देशिका के अंदर रखी जाएंगी, इसलिए इसे संस्करण नियंत्रण प्रणाली से बाहर रखना सबसे अच्छा है:
.gitignore
public/uploads
आप अपने अपलोडर के अंदर store_dir विधि को संशोधित करके कोई अन्य स्थान भी चुन सकते हैं।
इस बिंदु पर, हम फ़ाइलें अपलोड करना शुरू करने के लिए एक नया व्यू और फ़ॉर्म पार्शियल बना सकते हैं:
views/posts/new.html.erb
<h1>Add post</h1> <%= render 'form', post: @post %>
views/posts/_form.html.erb
<%= form_for post do |f| %>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div>
<%= f.label :body %>
<%= f.text_area :body %>
</div>
<div>
<%= f.label :image %>
<%= f.file_field :image %>
</div>
<%= f.submit %>
<% end %>
ध्यान दें कि PostsController को संशोधित करने की आवश्यकता नहीं है क्योंकि हमने पहले से ही image विशेषता की अनुमति दी है।
अंत में, संपादन व्यू बनाएं:
views/posts/edit.html.erb
<h1>Edit post</h1> <%= render 'form', post: @post %>
बस! आप सर्वर बूट कर सकते हैं और एक छवि के साथ पोस्ट बनाने का प्रयास कर सकते हैं। समस्या यह है कि यह छवि कहीं भी दिखाई नहीं देती है, इसलिए अगले अनुभाग पर चलते हैं और एक शो पेज जोड़ते हैं!
छवियों को प्रदर्शित करना
तो, एकमात्र व्यू जिसे हमने अभी तक नहीं बनाया है वह है show. इसे अभी जोड़ें:
views/posts/show.html.erb
<%= link_to 'All posts', posts_path %> <h1><%= @post.title %></h1> <%= image_tag(@post.image.url, alt: 'Image') if @post.image? %> <p><%= @post.body %></p> <p><%= link_to 'Edit', edit_post_path(@post) %></p>
जैसा कि आप देख सकते हैं, एक अटैचमेंट प्रदर्शित करना वास्तव में आसान है: आपको बस @post.image.url कहकर एक छवि का URL प्राप्त करना होगा। फ़ाइल का पथ प्राप्त करने के लिए, current_path विधि का उपयोग करें। ध्यान दें कि CarrierWave हमारे लिए एक image? विधि भी प्रदान करता है यह जांचने के लिए कि क्या कोई अटैचमेंट मौजूद है (भले ही फ़ाइल मौजूद न हो, image विधि कभी भी nil वापस नहीं करेगी)।
अब, एक पोस्ट पर नेविगेट करने के बाद, आपको एक छवि दिखाई देनी चाहिए, लेकिन यह बहुत बड़ी दिखाई दे सकती है: आखिरकार, हम कहीं भी आयामों को प्रतिबंधित नहीं कर रहे हैं। बेशक, हम CSS नियमों के साथ छवि को छोटा कर सकते थे, लेकिन फ़ाइल अपलोड होने के बाद थंबनेल उत्पन्न करना बेहतर है। हालाँकि, इसके लिए कुछ अतिरिक्त चरणों की आवश्यकता होती है।
थंबनेल उत्पन्न करना
छवियों को क्रॉप और स्केल करने के लिए, हमें एक अलग टूल की आवश्यकता होती है। बॉक्स से बाहर CarrierWave में RMagick और MiniMagick जेम के लिए समर्थन है जो बदले में ImageMagick की मदद से छवियों में हेरफेर करने के लिए उपयोग किए जाते हैं। ImageMagick एक ओपन-सोर्स समाधान है जो आपको मौजूदा छवियों को संपादित करने और नई छवियों को उत्पन्न करने की अनुमति देता है, इसलिए आगे बढ़ने से पहले आपको इसे डाउनलोड और इंस्टॉल करना होगा। अगला, आप दो जेम में से किसी को भी चुनने के लिए स्वतंत्र हैं। मैं MiniMagick के साथ रहूंगा, क्योंकि इसे इंस्टॉल करना बहुत आसान है और इसका बेहतर समर्थन है:
Gemfile
gem 'mini_magick'
चलाएं:
bundle install
फिर MiniMagick को अपने अपलोडर में शामिल करें:
uploaders/image_uploader.rb
include CarrierWave::MiniMagick
अब हमें बस अपने अपलोडर में एक नया संस्करण पेश करने की आवश्यकता है। संस्करण (या शैलियों) की अवधारणा कई फ़ाइल अपलोड लाइब्रेरी में उपयोग की जाती है; इसका सीधा सा मतलब है कि मूल अटैचमेंट के आधार पर अतिरिक्त फ़ाइलें बनाई जाएंगी, उदाहरण के लिए, विभिन्न आयामों या प्रारूपों के साथ। thumb नामक एक नया संस्करण पेश करें:
uploaders/image_uploader.rb
version :thumb do process resize_to_fill: [350, 350] end
आपके पास जितने चाहें उतने संस्करण हो सकते हैं और, क्या अधिक है, संस्करण अन्य संस्करणों के ऊपर भी बनाए जा सकते हैं:
uploaders/image_uploader.rb
version :small_thumb, from_version: :thumb do process resize_to_fill: [20, 20] end
यदि आपने पहले से ही कुछ छवियां अपलोड की हैं, तो उनके लिए थंबनेल उपलब्ध नहीं होंगे। यह कोई समस्या नहीं है, क्योंकि आप Rails कंसोल से उन्हें फिर से बना सकते हैं:
rails c
Post.find_each {|post| post.image.recreate_versions!(:thumb) if post.image?}
अंत में, मूल छवि के लिंक के साथ अपना थंबनेल प्रदर्शित करें:
views/posts/show.html.erb
<%= link_to(image_tag(@post.image.thumb.url, alt: 'Image'), @post.image.url, target: '_blank') if @post.image? %>
सर्वर बूट करें और परिणाम देखें!
वैलिडेशन जोड़ना
वर्तमान में हमारा अपलोड काम करता है, लेकिन हम उपयोगकर्ता इनपुट को बिल्कुल भी वैलिडेट नहीं कर रहे हैं, जो निश्चित रूप से बुरा है। जब तक हम केवल छवियों के साथ काम करना चाहते हैं, आइए .png, .jpg और .gif एक्सटेंशन को व्हाइटलिस्ट करें:
uploaders/image_uploader.rb
def extension_whitelist %w(jpg jpeg gif png) end
आप content_type_whitelist विधि को परिभाषित करके कंटेंट टाइप चेक भी जोड़ सकते हैं:
uploaders/image_uploader.rb
def content_type_whitelist /image\// end
वैकल्पिक रूप से, content_type_blacklist विधि को परिभाषित करके कुछ फ़ाइल प्रकारों को ब्लैकलिस्ट करना संभव है, उदाहरण के लिए निष्पादन योग्य।
फ़ाइल के प्रकार और एक्सटेंशन की जांच के अलावा, आइए इसे 1 मेगाबाइट से कम लागू करें। ऐसा करने के लिए, हमें ActiveModel के लिए फ़ाइल वैलिडेशन का समर्थन करने वाले एक अतिरिक्त जेम की आवश्यकता होगी:
Gemfile
gem 'file_validators'
इसे इंस्टॉल करें:
bundle install
अब वांछित वैलिडेशन पेश करें (ध्यान दें कि मैं title और body विशेषताओं के लिए चेक भी जोड़ रहा हूं):
models/post.rb
validates :title, presence: true, length: {minimum: 2}
validates :body, presence: true
validates :image, file_size: { less_than: 1.megabytes }
अगली बात CarrierWave के एरर मैसेज के लिए I18n अनुवाद जोड़ना है:
config/locales/en.yml
en:
errors:
messages:
carrierwave_processing_error: "Cannot resize image."
carrierwave_integrity_error: "Not an image."
carrierwave_download_error: "Couldn't download image."
extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
वर्तमान में, हम कहीं भी वैलिडेशन एरर प्रदर्शित नहीं करते हैं, इसलिए एक साझा पार्शियल बनाते हैं:
views/shared/_errors.html.erb
<% if object.errors.any? %>
<h3>Some errors were found:</h3>
<ul>
<% object.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
इस पार्शियल को फ़ॉर्म के अंदर उपयोग करें:
views/posts/_form.html.erb
<%= render 'shared/errors', object: post %>
अब कुछ अमान्य फ़ाइलें अपलोड करने का प्रयास करें और परिणाम देखें। यह काम करना चाहिए, लेकिन यदि आप एक वैध फ़ाइल चुनते हैं और शीर्षक या बॉडी नहीं भरते हैं, तो चेक अभी भी विफल होंगे और एक एरर प्रदर्शित होगा। हालाँकि, फ़ाइल फ़ील्ड साफ़ हो जाएगी और उपयोगकर्ता को फिर से छवि चुननी होगी, जो बहुत सुविधाजनक नहीं है। इसे ठीक करने के लिए, हमें फ़ॉर्म में एक और फ़ील्ड जोड़ने की आवश्यकता है।
रिक्वेस्ट के बीच फ़ाइलों को बनाए रखना
फ़ॉर्म के पुनर्प्रदर्शन के दौरान फ़ाइलों को बनाए रखना वास्तव में काफी आसान है। आपको बस एक नया हिडन फ़ील्ड जोड़ना है और इसे कंट्रोलर के अंदर अनुमति देनी है:
views/shared/_form.html.erb
<%= f.label :image %> <%= f.file_field :image %><br> <%= f.hidden_field :image_cache %>
posts_controller.rb
params.require(:post).permit(:title, :body, :image, :image_cache)
अब image_cache स्वचालित रूप से भर जाएगा और छवि खो नहीं जाएगी। एक थंबनेल प्रदर्शित करना भी उपयोगी हो सकता है ताकि उपयोगकर्ता समझ सके कि छवि सफलतापूर्वक संसाधित हो गई थी:
views/shared/_form.html.erb
<% if post.image? %> <%= image_tag post.image.thumb.url %> <% end %>
छवियों को हटाना
एक और बहुत आम विशेषता है रिकॉर्ड संपादित करते समय अटैच की गई फ़ाइलों को हटाने की क्षमता। CarrierWave के साथ, इस सुविधा को लागू करना कोई समस्या नहीं है। फ़ॉर्म में एक नया चेकबॉक्स जोड़ें:
views/shared/_form.html.erb
<% if post.image? %>
<%= image_tag post.image.thumb.url %>
<div>
<%= label_tag :remove_image do %>
Remove image
<%= f.check_box :remove_image %>
<% end %>
</div>
<% end %>
और remove_image विशेषता की अनुमति दें:
posts_controller.rb
params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache)
बस! मैन्युअल रूप से एक छवि को हटाने के लिए, remove_image! विधि का उपयोग करें:
@post.remove_image!
रिमोट स्थान से अपलोड करना
CarrierWave बॉक्स से बाहर एक बहुत अच्छी सुविधा भी प्रदान करता है: URL द्वारा रिमोट स्थानों से फ़ाइलें अपलोड करने की क्षमता। आइए एक नया फ़ील्ड जोड़कर और संबंधित विशेषता की अनुमति देकर इस क्षमता को अभी पेश करें:
views/shared/_form.html.erb
<%= f.text_field :remote_image_url %> <small>Enter URL to an image</small>
posts_controller.rb
params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache, :remote_image_url)
यह कितना अच्छा है? आपको बिल्कुल कोई बदलाव करने की आवश्यकता नहीं है, और आप इस सुविधा को तुरंत परीक्षण कर सकते हैं!
एकाधिक अपलोड के साथ काम करना
मान लीजिए हम चाहते हैं कि हमारे पोस्ट में कई अटैचमेंट उपलब्ध हों। वर्तमान सेटअप के साथ यह संभव नहीं है, लेकिन सौभाग्य से, CarrierWave ऐसे परिदृश्य का भी समर्थन करता है। इस सुविधा को लागू करने के लिए, आपको या तो एक सीरियलाइज़्ड फ़ील्ड (SQLite के लिए) या एक JSON फ़ील्ड (Postgres या MySQL के लिए) जोड़ने की आवश्यकता है। मैं बाद वाले विकल्प को पसंद करता हूं, इसलिए आइए अब एक नए डेटाबेस एडाप्टर पर स्विच करें। Gemfile से sqlite3 जेम को हटाएं और इसके बजाय pg जोड़ें:
Gemfile
gem 'pg'
इसे इंस्टॉल करें:
bundle install
डेटाबेस कॉन्फ़िगरेशन को इस तरह संशोधित करें:
config/database.yml
default: &default adapter: postgresql pool: 5 timeout: 5000 development: <<: *default database: upload_carrier_dev username: 'YOUR_USER' password: 'YOUR_PASSWORD' host: localhost
संबंधित Postgres डेटाबेस बनाएं, और फिर माइग्रेशन जनरेट करें और लागू करें:
rails g migration add_attachments_to_posts attachments:json rails db:migrate
यदि आप SQLite के साथ रहना पसंद करते हैं, तो CarrierWave के दस्तावेज़ीकरण में सूचीबद्ध निर्देशों का पालन करें।
अब अपलोडर्स को माउंट करें (बहुवचन रूप पर ध्यान दें!):
model/post.rb
mount_uploaders :attachments, ImageUploader
मैं अटैचमेंट के लिए वही अपलोडर उपयोग कर रहा हूं, लेकिन निश्चित रूप से आप एक अलग कॉन्फ़िगरेशन के साथ एक नया जनरेट कर सकते हैं।
अपने फ़ॉर्म में एकाधिक फ़ाइल फ़ील्ड जोड़ें:
views/shared/_form.html.erb
<div> <%= f.label :attachments %> <%= f.file_field :attachments, multiple: true %> </div>
चूंकि attachments फ़ील्ड में एक सरणी होगी, इसे निम्न तरीके से अनुमति दी जानी चाहिए:
posts_controller.rb
params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache, :remote_image_url, attachments: [])
अंत में, आप पोस्ट के अटैचमेंट पर पुनरावृति कर सकते हैं और उन्हें हमेशा की तरह प्रदर्शित कर सकते हैं:
views/shared/show.html.erb
<% if @post.attachments? %>
<ul>
<% @post.attachments.each do |attachment| %>
<li><%= link_to(image_tag(attachment.thumb.url, alt: 'Image'), attachment.url, target: '_blank') %></li>
<% end %>
</ul>
<% end %>
ध्यान दें कि प्रत्येक अटैचमेंट में हमारे ImageUploader में कॉन्फ़िगर किए अनुसार एक थंबनेल होगा। अच्छा!
क्लाउड स्टोरेज का उपयोग करना
फ़ाइल स्टोरेज के साथ रहना हमेशा सुविधाजनक और/या संभव नहीं होता है क्योंकि, उदाहरण के लिए, Heroku पर कस्टम फ़ाइलों को संग्रहीत करना संभव नहीं है। इसलिए आप पूछ सकते हैं कि CarrierWave को Amazon S3 क्लाउड स्टोरेज के साथ कैसे जोड़ा जाए? खैर, यह भी एक बहुत आसान काम है। CarrierWave इस सुविधा को लागू करने के लिए fog-aws जेम पर निर्भर करता है:
Gemfile
gem "fog-aws"
इसे इंस्टॉल करें:
bundle install
आइए CarrierWave के लिए एक इनिशियलाइज़र बनाएं और क्लाउड स्टोरेज को वैश्विक रूप से कॉन्फ़िगर करें:
config/initializers/carrierwave.rb
CarrierWave.configure 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']
end
कुछ अन्य विकल्प उपलब्ध हैं, जिन्हें दस्तावेज़ीकरण में पाया जा सकता है।
मैं पर्यावरण चर को सुरक्षित तरीके से सेट करने के लिए dotenv-rails जेम का उपयोग कर रहा हूं, लेकिन आप कोई अन्य विकल्प चुन सकते हैं। हालांकि, सुनिश्चित करें कि आपकी S3 कुंजी जोड़ी सार्वजनिक रूप से उपलब्ध नहीं है, क्योंकि अन्यथा कोई भी आपके बकेट में कुछ भी अपलोड कर सकता है!
अब, storage :file लाइन को इसके साथ बदलें:
uploaders/image_uploader.rb
storage :fog
S3 के अलावा, CarrierWave Google Storage और Rackspace पर अपलोड का भी समर्थन करता है। इन सेवाओं को सेट करना भी आसान है।
निष्कर्ष
आज के लिए बस इतना ही! हमने CarrierWave की सभी प्रमुख विशेषताओं को कवर किया है, और अब आप इसे अपनी परियोजनाओं में उपयोग करना शुरू कर सकते हैं। इसमें कुछ अतिरिक्त विकल्प उपलब्ध हैं, इसलिए दस्तावेज़ीकरण ब्राउज़ करें।
यदि आप फंस जाते हैं, तो अपने प्रश्न पोस्ट करने में संकोच न करें। इसके अलावा, CarrierWave के विकी पर एक नज़र डालना उपयोगी हो सकता है, जो कई सामान्य प्रश्नों का उत्तर देने वाले उपयोगी "कैसे करें" लेखों की मेजबानी करता है।
मेरे साथ बने रहने के लिए धन्यवाद, और हैप्पी कोडिंग!