นี่เป็นอีกบทความหนึ่งในซีรีส์ "การอัปโหลดด้วย Rails" วันนี้เราจะมาพบกับ Carrierwave หนึ่งในโซลูชั่นการอัพโหลดไฟล์ยอดนิยมสำหรับ Rails ฉันชอบ Carrierwave เพราะมันง่ายต่อการเริ่มต้น มีคุณสมบัติมากมายที่พร้อมใช้งาน และมีบทความ "วิธีการ" มากมายที่เขียนโดยสมาชิกของชุมชน ดังนั้นคุณจะไม่หลงทาง
ในบทความนี้ คุณจะได้เรียนรู้วิธี:
- รวม Carrierwave เข้ากับแอป Rails ของคุณ
- เพิ่มการตรวจสอบ
- คงไฟล์ข้ามคำขอ
- ลบไฟล์
- สร้างภาพขนาดย่อ
- อัปโหลดไฟล์จากสถานที่ห่างไกล
- แนะนำการอัปโหลดหลายไฟล์
- เพิ่มการรองรับที่เก็บข้อมูลบนคลาวด์
ซอร์สโค้ดสำหรับบทความนี้มีอยู่ใน GitHub ขอให้สนุกกับการอ่าน!
การวางรากฐาน
เช่นเคย ให้เริ่มต้นด้วยการสร้างแอปพลิเคชัน Rails ใหม่:
รางใหม่ 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 ชื่อโพสต์:string body:text image:stringrails db:migrate
ตั้งค่าบางเส้นทาง:
config/routes.rb
ทรัพยากร :postsroot ถึง:'posts#index'
นอกจากนี้ ให้สร้างตัวควบคุมพื้นฐาน:
posts_controller.rb
คลาส PostsControllerตอนนี้ มาสร้าง index . กัน มุมมอง:
views/posts/index.html.erb
โพสต์
<%=link_to 'เพิ่มโพสต์', new_post_path %><%=แสดงผล @posts %>และบางส่วนที่เกี่ยวข้อง:
views/posts/_post.html.erb
<%=link_to post.title, post_path(post) %>
<%=truncate(post.body, length:150) %>
<%=link_to 'แก้ไข', edit_post_path(โพสต์) %>
ฉันใช้ Rails
truncate
วิธีแสดงเฉพาะ 150 สัญลักษณ์แรกจากโพสต์ ก่อนที่เราจะสร้างมุมมองอื่นและบางส่วนของแบบฟอร์ม เรามารวม Carrierwave เข้ากับแอปพลิเคชันก่อนการรวม Carrierwave
วางอัญมณีใหม่ลงใน Gemfile :
Gemfile
อัญมณี 'carrierwave', '~> 1.0'วิ่ง:
การติดตั้งมัดCarrierwave เก็บการกำหนดค่าไว้ภายใน ตัวอัปโหลด ที่รวมอยู่ในโมเดลของคุณ หากต้องการสร้างตัวอัปโหลด ให้ใช้คำสั่งต่อไปนี้:
รางสร้างอิมเมจอัพโหลดตอนนี้ใน app/uploaders คุณจะพบไฟล์ใหม่ที่ชื่อว่า image_uploader.rb . โปรดทราบว่ามีความคิดเห็นและตัวอย่างที่เป็นประโยชน์ คุณจึงอาจใช้เพื่อเริ่มต้น ในการสาธิตนี้ เราจะใช้ ActiveRecord แต่ Carrierwave ยังรองรับ Mongoid, Sequel และ DataMapper ด้วย
ต่อไปเราต้องรวมหรือ เมานต์ ผู้อัปโหลดนี้เข้าสู่โมเดล:
รุ่น/post.rb
mount_uploader :image, ImageUploaderผู้อัปโหลดมีการตั้งค่าเริ่มต้นที่เหมาะสมอยู่แล้ว แต่อย่างน้อยที่สุด เราจำเป็นต้องเลือกตำแหน่งที่จะเก็บไฟล์ที่อัปโหลดไว้ ตอนนี้ ลองใช้พื้นที่จัดเก็บไฟล์กัน:
uploaders/image_uploader.rb
ที่เก็บข้อมูล :ไฟล์โดยค่าเริ่มต้น ไฟล์จะถูกวางไว้ใน สาธารณะ/อัปโหลด ไดเร็กทอรี ดังนั้นจึงเป็นการดีที่สุดที่จะแยกออกจากระบบควบคุมเวอร์ชัน:
.gitignore
สาธารณะ/อัปโหลดคุณยังสามารถแก้ไข
store_dir
วิธีภายในตัวอัปโหลดของคุณเพื่อเลือกตำแหน่งอื่นณ จุดนี้ เราสามารถสร้างมุมมองใหม่และแบบฟอร์มบางส่วนเพื่อเริ่มการอัปโหลดไฟล์:
จำนวนการดู/โพสต์/new.html.erb
เพิ่มโพสต์
<%=แสดงผล 'แบบฟอร์ม' โพสต์:@post %>จำนวนการดู/โพสต์/_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 %><% สิ้นสุด %>โปรดทราบว่า
PostsController
ไม่จำเป็นต้องแก้ไข เพราะเราอนุญาตimage
. แล้ว แอตทริบิวต์สุดท้าย สร้างมุมมองแก้ไข:
จำนวนการดู/โพสต์/edit.html.erb
แก้ไขโพสต์
<%=แสดง 'แบบฟอร์ม' โพสต์:@post %>แค่นั้นแหละ! คุณสามารถบูตเซิร์ฟเวอร์และพยายามสร้างโพสต์ด้วยรูปภาพ ปัญหาคือภาพนี้ไม่สามารถมองเห็นได้ทุกที่ ดังนั้น ไปที่ส่วนถัดไปและเพิ่มหน้าการแสดง!
การแสดงภาพ
ดังนั้น มุมมองเดียวที่เรายังไม่ได้สร้างคือ แสดง . เพิ่มเลย:
จำนวนการดู/โพสต์/show.html.erb
<%=link_to 'โพสต์ทั้งหมด', posts_path %><%[email protected] %>
<%=image_tag(@post.image.url, alt:'Image') ถ้า @โพสต์ภาพ? %><%[email protected] %>
<%=link_to 'Edit', edit_post_path(@post) %>
อย่างที่คุณเห็น การแสดงไฟล์แนบนั้นง่ายมาก สิ่งที่คุณต้องทำคือพูดว่า
@post.image.url
เพื่อคว้า URL ของรูปภาพ ในการรับเส้นทางไปยังไฟล์ ให้ใช้current_path
กระบวนการ. โปรดทราบว่า Carrierwave ยังมีimage?
วิธีให้เราตรวจสอบว่ามีสิ่งที่แนบมาหรือไม่ (image
เมธอดเองจะไม่ส่งคืนnil
แม้ว่าไฟล์นั้นจะไม่มีอยู่ก็ตาม)ตอนนี้ หลังจากนำทางไปยังโพสต์ คุณจะเห็นรูปภาพ แต่รูปภาพอาจดูใหญ่เกินไป ท้ายที่สุด เราไม่ได้จำกัดขนาดไว้ที่ใด แน่นอน เราสามารถย่อขนาดรูปภาพด้วยกฎ CSS บางอย่างได้ แต่จะดีกว่ามากที่จะสร้างภาพขนาดย่อหลังจากที่อัปโหลดไฟล์แล้ว อย่างไรก็ตาม สิ่งนี้ต้องมีขั้นตอนเพิ่มเติม
กำลังสร้างภาพขนาดย่อ
ในการครอบตัดและปรับขนาดรูปภาพ เราจำเป็นต้องมีเครื่องมือแยกต่างหาก นอกกรอบ Carrierwave รองรับอัญมณี RMagick และ MiniMagick ซึ่งในทางกลับกัน ใช้เพื่อจัดการภาพด้วยความช่วยเหลือของ ImageMagick ImageMagick เป็นโซลูชันโอเพนซอร์ซที่ให้คุณแก้ไขรูปภาพที่มีอยู่และสร้างรูปภาพใหม่ ดังนั้นก่อนดำเนินการต่อ คุณต้องดาวน์โหลดและติดตั้ง ต่อไป คุณสามารถเลือกอัญมณีใดก็ได้จากสองอัญมณี ฉันจะใช้ MiniMagick ต่อไป เพราะติดตั้งได้ง่ายกว่ามากและมีการรองรับที่ดีกว่า:
Gemfile
อัญมณี 'mini_magick'วิ่ง:
การติดตั้งมัดจากนั้นรวม MiniMagick ไว้ในเครื่องมืออัปโหลดของคุณ:
uploaders/image_uploader.rb
รวม 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:
ราง cPost.find_each {|โพสต์| post.image.recreate_versions!(:thumb) ถ้า post.image?}สุดท้าย แสดงภาพขนาดย่อพร้อมลิงก์ไปยังภาพต้นฉบับ:
จำนวนการดู/โพสต์/show.html.erb
<%=link_to(image_tag(@post.image.thumb.url, alt:'Image'), @post.image.url, เป้าหมาย:'_blank') ถ้า @post.image? %>บูตเซิร์ฟเวอร์และสังเกตผลลัพธ์!
กำลังเพิ่มการตรวจสอบ
ขณะนี้การอัปโหลดของเราใช้งานได้ แต่เราไม่ได้ตรวจสอบข้อมูลของผู้ใช้เลย ซึ่งแน่นอนว่าแย่ ตราบใดที่เราต้องการทำงานกับรูปภาพเท่านั้น ให้อนุญาตส่วนขยาย .png, .jpg และ .gif:
uploaders/image_uploader.rb
def extension_whitelist %w(jpg jpeg gif png)สิ้นสุดคุณยังเพิ่มการตรวจสอบประเภทเนื้อหาได้ด้วยการกำหนด
content_type_whitelist
วิธีการ:uploaders/image_uploader.rb
def content_type_whitelist /image\//endอีกทางหนึ่ง เป็นไปได้ที่จะขึ้นบัญชีดำไฟล์บางประเภท เช่น ไฟล์สั่งการ โดยกำหนด
content_type_blacklist
วิธีการนอกจากการตรวจสอบประเภทและนามสกุลของไฟล์แล้ว ให้บังคับให้มีขนาดน้อยกว่า 1 เมกะไบต์ ในการดำเนินการนี้ เราจำเป็นต้องมีการตรวจสอบไฟล์ที่รองรับ gem เพิ่มเติมสำหรับ ActiveModel:
Gemfile
อัญมณี 'file_validators'ติดตั้ง:
การติดตั้งมัดตอนนี้แนะนำการตรวจสอบที่ต้องการ (โปรดทราบว่าฉันกำลังเพิ่มการตรวจสอบสำหรับ
title
และbody
แอตทริบิวต์):รุ่น/post.rb
ตรวจสอบ :ชื่อ สถานะ:จริง ความยาว:{ขั้นต่ำ:2}ตรวจสอบ :เนื้อหา สถานะ:truevalidates :image, file_size:{ น้อยกว่า:1.เมกะไบต์ }สิ่งต่อไปที่ต้องทำคือเพิ่มการแปล I18n สำหรับข้อความแสดงข้อผิดพลาดของ Carrierwave:
config/locales/en.yml
th:ข้อผิดพลาด:ข้อความ:carrierwave_processing_error:"ไม่สามารถปรับขนาดรูปภาพได้" carrierwave_integrity_error:"ไม่ใช่รูปภาพ" carrierwave_download_error:"ไม่สามารถดาวน์โหลดภาพได้" extension_whitelist_error:"คุณไม่ได้รับอนุญาตให้อัปโหลดไฟล์ %{extension} ประเภทที่อนุญาต:%{allowed_types}" extension_blacklist_error:"คุณไม่ได้รับอนุญาตให้อัปโหลดไฟล์ %{extension} ประเภทต้องห้าม:%{prohibited_types}"ขณะนี้ เราไม่แสดงข้อผิดพลาดในการตรวจสอบที่ใด ดังนั้นเรามาสร้างส่วนที่ใช้ร่วมกัน:
views/shared/_errors.html.erb
<ก่อน><% ถ้า object.errors.any? %>พบข้อผิดพลาดบางอย่าง:
<% object.errors.full_messages.each do |message| %>
<% สิ้นสุด %>- <%=ข้อความ %>
<% สิ้นสุด %>
ใช้ส่วนนี้ในแบบฟอร์ม:
จำนวนการดู/โพสต์/_form.html.erb
<%=แสดงผล 'แชร์/ข้อผิดพลาด' วัตถุ:โพสต์ %>
ตอนนี้ลองอัปโหลดไฟล์ที่ไม่ถูกต้องและสังเกตผลลัพธ์ ควรใช้งานได้ แต่ถ้าคุณเลือกไฟล์ที่ถูกต้องและไม่กรอกชื่อหรือเนื้อหา การตรวจสอบจะยังล้มเหลวและข้อผิดพลาดจะปรากฏขึ้น อย่างไรก็ตาม ฟิลด์ไฟล์จะถูกล้างออก และผู้ใช้จะต้องเลือกภาพอีกครั้ง ซึ่งไม่สะดวกนัก ในการแก้ไข เราต้องเพิ่มฟิลด์อื่นในแบบฟอร์ม
การคงไฟล์ข้ามคำขอ
การคงไฟล์ไว้ในรูปแบบการแสดงซ้ำนั้นค่อนข้างง่าย สิ่งที่คุณต้องทำคือเพิ่มฟิลด์ที่ซ่อนอยู่ใหม่และอนุญาตภายในคอนโทรลเลอร์:
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)
ตอนนี้ image_cache
จะถูกเติมโดยอัตโนมัติและภาพจะไม่สูญหาย การแสดงภาพขนาดย่อด้วยอาจเป็นประโยชน์เพื่อให้ผู้ใช้เข้าใจว่าภาพได้รับการประมวลผลสำเร็จ:
views/shared/_form.html.erb
<ก่อน><% ถ้า post.image? %> <%=image_tag post.image.thumb.url %><% สิ้นสุด %>การลบภาพ
คุณสมบัติทั่วไปอีกอย่างหนึ่งคือความสามารถในการลบไฟล์ที่แนบมาเมื่อแก้ไขบันทึก ด้วย Carrierwave การใช้คุณลักษณะนี้ไม่เป็นปัญหา เพิ่มช่องทำเครื่องหมายใหม่ให้กับแบบฟอร์ม:
views/shared/_form.html.erb
<ก่อน><% ถ้า post.image? %> <%=image_tag post.image.thumb.url %>
และขออนุญาต 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 %>ป้อน URL ไปยังรูปภาพ
posts_controller.rb
params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache, :remote_image_url)
มันเจ๋งแค่ไหน? คุณไม่จำเป็นต้องทำการเปลี่ยนแปลงใดๆ และคุณสามารถทดสอบคุณลักษณะนี้ได้ทันที!
การทำงานกับการอัปโหลดหลายรายการ
สมมติว่าเราต้องการให้โพสต์ของเรามีไฟล์แนบหลายไฟล์ ด้วยการตั้งค่าปัจจุบันไม่สามารถทำได้ แต่โชคดีที่ Carrierwave รองรับสถานการณ์ดังกล่าวเช่นกัน ในการใช้งานคุณลักษณะนี้ คุณต้องเพิ่มฟิลด์ซีเรียลไลซ์ (สำหรับ SQLite) หรือฟิลด์ JSON (สำหรับ Postgres หรือ MySQL) ฉันชอบตัวเลือกหลัง เลยเปลี่ยนเป็นอะแดปเตอร์ฐานข้อมูลใหม่เดี๋ยวนี้ ลบ sqlite3 gem ออกจาก Gemfile และเพิ่ม pg แทน:
Gemfile
อัญมณี 'pg'
ติดตั้ง:
การติดตั้งมัด
แก้ไขการกำหนดค่าฐานข้อมูลดังนี้:
config/database.yml
default:&default adapter:postgresql pool:5 timeout:5000development:<<:*default database:upload_carrier_dev username:'YOUR_USER' password:'YOUR_PASSWORD' host:localhost
สร้างฐานข้อมูล Postgres ที่เกี่ยวข้อง จากนั้นจึงสร้างและใช้การย้ายข้อมูล:
การโยกย้าย rails g add_attachments_to_posts attachments:jsonrails db:migrate
หากคุณต้องการใช้ SQLite ต่อไป ให้ทำตามคำแนะนำที่ระบุไว้ในเอกสารประกอบของ Carrierwave
ตอนนี้เมานต์ผู้อัปโหลด (สังเกตรูปพหูพจน์!):
รุ่น/post.rb
mount_uploaders :สิ่งที่แนบมา, ImageUploader
ฉันใช้เครื่องมืออัปโหลดเดียวกันสำหรับไฟล์แนบ แต่แน่นอนว่าคุณสามารถสร้างไฟล์แนบใหม่ด้วยการกำหนดค่าที่ต่างออกไปได้
เพิ่มช่องไฟล์หลายช่องในแบบฟอร์มของคุณ:
views/shared/_form.html.erb
<%=f.label :attachments %> <%=f.file_field :attachments, หลายรายการ:true %>
ตราบใดที่attachments
ฟิลด์กำลังจะมีอาร์เรย์ ซึ่งควรได้รับอนุญาตในลักษณะต่อไปนี้:
posts_controller.rb
params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache, :remote_image_url, ไฟล์แนบ:[])
สุดท้ายนี้ คุณสามารถทำซ้ำสิ่งที่แนบมาของโพสต์และแสดงได้ตามปกติ:
views/shared/show.html.erb
<% ถ้า @post.attachments? %>
- <% @post.attachments.each do |attachment| %>
- <%=link_to(image_tag(attachment.thumb.url, alt:'Image'), attachment.url, เป้าหมาย:'_blank') %> <% end %>
โปรดทราบว่าไฟล์แนบแต่ละไฟล์จะมีภาพขนาดย่อตามที่กำหนดค่าไว้ใน ImageUploader
. เยี่ยมเลย!
การใช้ที่เก็บข้อมูลบนคลาวด์
การจัดเก็บไฟล์อาจไม่สะดวกและ/หรือเป็นไปได้เสมอไป ตัวอย่างเช่น ใน Heroku จะไม่สามารถจัดเก็บไฟล์ที่กำหนดเองได้ ดังนั้นคุณอาจถามว่าจะแต่งงานกับ Carrierwave กับที่เก็บข้อมูลบนคลาวด์ของ Amazon S3 ได้อย่างไร นั่นเป็นงานที่ค่อนข้างง่ายเช่นกัน Carrierwave พึ่งพา Fog-aws gem เพื่อใช้งานฟีเจอร์นี้:
Gemfile
อัญมณี "หมอก-aws"
ติดตั้ง:
การติดตั้งมัด
มาสร้างตัวเริ่มต้นสำหรับ Carrierwave และกำหนดค่าที่เก็บข้อมูลบนคลาวด์ทั่วโลก:
config/initializers/carrierwave.rb
CarrierWave.configure ทำ |config| config.fog_provider ='fog/aws' config.fog_credentials ={ ผู้ให้บริการ:'AWS', aws_access_key_id:ENV['S3_KEY'], aws_secret_access_key:ENV['S3_SECRET'] ภูมิภาค:ENV['S3_REGION'] fog_directory =ENV['S3_BUCKET']สิ้นสุด
มีตัวเลือกอื่นๆ ซึ่งสามารถพบได้ในเอกสารประกอบ
ฉันใช้ dotenv-rails gem เพื่อตั้งค่าตัวแปรสภาพแวดล้อมด้วยวิธีที่ปลอดภัย แต่คุณสามารถเลือกตัวเลือกอื่นได้ อย่างไรก็ตาม ตรวจสอบให้แน่ใจว่าคู่คีย์ S3 ของคุณไม่เปิดเผยต่อสาธารณะ เพราะไม่เช่นนั้นใครๆ ก็สามารถอัปโหลดอะไรก็ได้ไปยังบัคเก็ตของคุณ!
ถัดไป แทนที่ storage :file
สอดคล้องกับ:
uploaders/image_uploader.rb
ที่เก็บข้อมูล :fog
นอกเหนือจาก S3 แล้ว Carrierwave ยังรองรับการอัปโหลดไปยัง Google Storage และ Rackspace บริการเหล่านี้ติดตั้งง่ายเช่นกัน
บทสรุป
นี่แหละสำหรับวันนี้! เราได้ครอบคลุมคุณสมบัติหลักทั้งหมดของ Carrierwave แล้ว และตอนนี้ คุณสามารถเริ่มใช้งานได้ในโครงการของคุณ มีตัวเลือกเพิ่มเติมบางอย่าง ดังนั้นโปรดดูเอกสารประกอบ
หากคุณติดขัด อย่าลังเลที่จะโพสต์คำถามของคุณ นอกจากนี้ อาจเป็นประโยชน์ที่จะดูวิกิของ Carrierwave ซึ่งมีบทความ "วิธีการ" ที่เป็นประโยชน์ซึ่งตอบคำถามทั่วไปมากมาย
ดังนั้นฉันขอขอบคุณที่อยู่กับฉันและขอให้สนุกกับการเขียนโค้ด!