Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การอัพโหลดด้วย Rails และ Carrierwave

นี่เป็นอีกบทความหนึ่งในซีรีส์ "การอัปโหลดด้วย 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 %>

<%=@post.title %>

<%=image_tag(@post.image.url, alt:'Image') ถ้า @โพสต์ภาพ? %>

<%=@post.body %>

<%=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 %>
<%=label_tag :remove_image ทำ %> ลบรูปภาพ <%=f.check_box :remove_image %> <% สิ้นสุด %>
<% จบ %>

และขออนุญาต 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 ซึ่งมีบทความ "วิธีการ" ที่เป็นประโยชน์ซึ่งตอบคำถามทั่วไปมากมาย

ดังนั้นฉันขอขอบคุณที่อยู่กับฉันและขอให้สนุกกับการเขียนโค้ด!