Category: การพัฒนา Web Application

  • สร้างกราฟด้วย Chart.js ร่วมกับ ASP.NET

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

        สำหรับ Chart.js นั้นเป็นเฟรมเวิร์คที่สร้างด้วยภาษา JavaScript ดังนั้นคุณสมบัติที่พ่วงมาด้วยคือการทำงานแบบ AJAX อย่างเต็มรูปแบบ ซึ่งในบทความนี้จะเป็นการนำเอามาใช้ร่วมกับเว็บแอพลิเคชัน ที่พัฒนาด้วย ASP.NET โดยวิธีการส่งข้อมูลในรูปแบบ JSON ผ่าน Web Services และทำการแปลงข้อมูล JSON ให้อยู่ในรูปแบบของ Object ที่มีโครงสร้างตรงตามที่ Chart.js ต้องการ เพื่อนำไปใช้แสดงผลในรูปแบบกราฟแท่ง กราฟเส้น กราฟวงกลม  ผ่าน HTML Tag ที่มีชื่อว่า Canvas ซึ่งมีการเพิ่มส่วนแสดงความหมายของสีต่างๆในกราฟ ที่ได้พัฒนาเพิ่มเติมขึ้นมาอีกด้วย

        ในบทความนี้จะยกตัวอย่างการสร้างกราฟใน 3 รูปแบบ และมีการกำหนดค่า Option ในการแสดงผลเท่าที่จำเป็นเท่านั้น สามารถอ่านวิธีการใช้งานเต็มรูปแบบได้ที่ http://www.chartjs.org/docs

    1. ทำการ Include ไฟล์ Javascript ของ chart.js ไว้ใน header (ในตัวอย่างเป็นการเรียกใช้งานจาก CDN หากต้องการใช้งานจากไฟล์ที่เก็บไว้ที่เว็บเซิฟเวอร์ให้ปรับแก้ src path)
    <script src="https://code.jquery.com/jquery-1.11.3.min.js" type="text/javascript"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js" type="text/javascript"></script>
    1. เพิ่ม canvas ในส่วนของ body
     <canvas id="report" width="600" height="300"></canvas>
    1. เขียน JavaScript เพื่อสร้างข้อมูล ตามโครงสร้างของกราฟแต่ละประเภท ในตัวอย่างจะเป็นโครงสร้างข้อมูลของกราฟแท่ง
     var dataDemo = { labels: ["Apple", "Sumsung", "ASUS", "OPPO"],
     datasets: [{
     label: "2557",
     fillColor: "#5B90BF",
     data: [20, 10, 9, 8]
     }]
     };
    1. ทำการสร้าง Context จาก canvas เพื่อนำไปสร้าง Chart Object โดยใช้คำสั่ง ดังนี้
     var ctx = $("#report").get(0).getContext("2d");
     var chart = new Chart(ctx).Bar(dataDemo, { bezierCurve: false });

     

    จาก 4 ขั้นตอนดังกล่าวเราจะได้โค้ดที่เมื่อทำการเพิ่มโครงสร้างของหน้าเว็บ และบันทึกเป็นไฟล์ html แบบนี้ chart.html  ลองเปิดดูจะพบว่าสามารถแสดงกราฟแท่งได้แล้ว

     

        แต่ในงานจริงนั้น รู้กันดีนะครับ ว่ามันไม่ง่ายอย่างนั้น โจทย์ของเราคือ สามารถนำข้อมูลจาก Database ซึ่งได้เขียน Query จนได้ข้อมูลแบบที่เราเรียกกันติดปากว่า Crosstab นั้นคือมีชื่อฟิลด์ข้อมูลที่มีความสัมพันธ์กันในแกน x และแกน y ซึ่งเป็นรูปแบบข้อมูลที่นำมาสร้างเป็นกราฟนั้นเอง ส่วนวิธีการนั้น ผมไม่ขอลงรายละเอียดในบทความนี้ แต่สำหรับท่านใดที่ใช้งานฐานข้อมูล Oracle ตั้งแต่ 11g ขึ้นไปลองศึกษาคำสั่งชื่อ Pivot ดูครับ น่าจะช่วยลดความยุ่งยากในขั้นตอนนี้ได้มาก ตัวอย่างข้อมูล แบบ Crosstab ที่เราจะส่งจากฝั่ง Server มาสร้างเป็น Object สำหรับสร้างกราฟอีกทีครับ

     

    crosstab-datatable

     

        อีกจุดที่มีความยุ่งยาก คือการแปลงจากรูปแบบข้อมูล Crosstab Datatable เป็น JSON ตัวช่วยของผมในเรื่องนี้คือ Library ที่ชื่อ Newtonsoft.Json สามารถดาวส์โหลดมาใช้งานผ่าน NuGet ได้เลยครับ จากนั้นก็เรียกใช้งาน ดังตัวอย่าง โดยตัวแปล data คือ Datatable ของเรา เมื่อได้ข้อมูล JSON แล้วให้ทำการสร้าง Web Service เพื่อให้สามารถดึงข้อมูลดังกล่าวจาก Javascript ได้

    JsonConvert.SerializeObject(data)

    ถัดจากนั้น ก็คือการนำ JSON นั้นไปสร้างเป็น Object ที่มีโครงสร้างตามที่ Chart.js ต้องการอีกที ในขั้นตอนนี้ผมลองหา Library ดูแล้วแต่ไม่เจอ ก็ได้พัฒนาเป็นชุดคำสั่ง helper เล็กๆ ซึ่งค่อนข้างจะจำเพาะกับลักษณะข้อมูล และรูปแบบการแสดงผลของเว็บไซด์ที่ผมพัฒนาอยู่ แต่สามารถนำไปปรับแก้ให้เหมาะสมได้ครับ ซึ่งในไฟล์นี้ หากใส่ parameter และเขียนโครงสร้าง html ตามตัวอย่าง เราจะเหลือโค้ดที่จะต้องเขียนเพียงไม่กี่บรรทัด ก็จะได้กราฟมาใช้งานใน 3 รูปแบบ คือ กราฟแท่ง กราฟเส้น และกราฟวงกลม ตามที่มี method ให้ครับ

     

    สำหรับการสร้างกราฟจะมีขั้นตอนเปลี่ยนไปดังนี้ครับ

    1. ทำการ include ไฟล์ Javascript โดยเพิ่มเติม chart-helper.js
    <script src="https://code.jquery.com/jquery-1.11.3.min.js" type="text/javascript"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js" type="text/javascript"></script>
    <script src="chart-helper.js" type="text/javascript"></script>
    1. ทำการ include ไฟล์ Style Sheet ดังนี้ bootstrap-grid.css และไฟล์ chart.css
    <link href="bootstrap-grid.css" rel="stylesheet" type="text/css" />
    <link href="chart.css" rel="stylesheet" type="text/css" />
    1. สร้าง HTML ตามโครงสร้าง โดยมีการใช้งาน class ในส่วน grid system ของ Bootstrap มาช่วยวางตำแหน่งซ้ายขวา ดังนี้
    <div class="row">
     <div class="col-xs-8">
     <canvas id="reportBar" width="600" height="300">
     </canvas>
     </div>
     <div class="col-xs-4">
     <ul id="chartLabelBar" class="chart-label-list">
     </ul>
     </div>
     </div>
     <div class="row">
     <div class="col-xs-8">
     <canvas id="reportLine" width="600" height="300">
     </canvas>
     </div>
     <div class="col-xs-4">
     <ul id="chartLabelLine" class="chart-label-list">
     </ul>
     </div>
     </div>
     <div class="row">
     <div class="col-xs-8">
     <canvas id="reportPie" width="600" height="300">
     </canvas>
     </div>
     <div class="col-xs-4">
     <ul id="chartLabelPie" class="chart-label-list">
     </ul>
     </div>
     </div>
    1. จากนั้นทำการเรียกใช้งาน Method ของ chart-helper.js ซึ่งประกอบไปด้วย CreateBarChart (สร้างกราฟแท่ง), CreateLineChart (สร้างกราฟเส้น), CreatePieChart (สร้างกราฟวงกลม) จะรับค่าเป็นข้อมูล JSON และ ID ของ HTML เพื่อนำไปสร้างกราฟโดยตัวอย่างการใช้งาน Method เหล่านี้ร่วมกับ Webservice (ทั้งนี้รูปแบบการเรียก Webservice ขึ้นอยู่กับวิธีการ Response ด้วย) มีดังนี้
    $.ajax({
     type: "POST",
     url: "DemoService.asmx/MethodName",
     data: { param:data1 }
     }).done(function (json) {
     CreateBarChart(json, "reportBar", "chartLabelBar");
     CreateLineChart(json, "reportLine", "chartLabelLine");
     CreatePieChart(json, "reportPie", "chartLabelPie");
     }).fail(function (jqXHR, textStatus) {
     alert(textStatus);
     });

    สามารถดาวโหลดไฟล์ตัวอย่างแบบเต็มได้ตามนี้ครับ chart-with-helper.html และกราฟที่ได้จะมีหน้าตาแบบนี้ ทั้งนี้ขึ้นอยู่กับข้อมูลของเราด้วยครับ

    bar line pie

     

  • วิธีการทดสอบเว็บไซต์ Responsive บน Smart phone ด้วย Chrome

    “ปัจจุบันกระแสการออกแบบเว็บเชิงตอบสนอง (Responsive design) ถูกนำมาใช้ในการออกแบบเว็บสมัยใหม่ เนืองจากสามารถดูได้ทั้งแบบผ่านเครื่องคอม แท็บเล็ต และมือถือ ได้โดยทันที”

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

    ซึ่งในส่วนที่ Chrome มีเครื่องมือที่ช่วยในการแสดงผลเว็บไซต์บนอุปกรณ์ Smart phone ได้ โดยไม่ต้องโหลดเพิ่ม แต่ประการใด !!! แถมวิธีการก็ง่ายแสนง่าย 

    ขั้นตอนที่ 1

    ให้ไปที่ More tools > Developer tools ดังภาพ

    01

    ขั้นตอนที่ 2

    เลือกที่รูปโทรศัพท์ ดังภาพ

    02

    ขั้นตอนที่ 3

    สังเกต ด้านซ้ายจะปรากฏหน้าจอมือถือขึ้นมา ให้ระบุ URL ที่เราต้องการดังภาพ

    06

    จากภาพ จะเห็นว่าหากเป็นเว็บที่ออกแบบด้วยหลักการออกแบบเว็บเชิงตอบสนอง (Responsive design) จะมีการจัดหน้าจอให้เหมาะสมกับอุปกรณ์

    ขั้นตอนที่ 4

    สังเกต ด้านบน เราสามารถเลือกรุ่นของ Smart Phone ได้หลายรุ่น แม้จะไม่มาก แต่ก็เป็นรุ่นหลักและหลากหลายความละเอียด หรือจะเลือกปรับขนาดเองก็ทำได้  และสามารถปรับให้แสดงแนวตั้งและแนวนอนได้อีกด้วย ดังภาพ

    07

    จะเห็นว่าเครื่องมือด้งกล่าวใช้งานไม่ยาก ทำให้สามารถดูหน้าจอในหลายๆ ความละเอียดได้อย่างรวดเร็ว 

  • ทดสอบการแสดงผลเว็บแอพพลิเคชันง่ายๆ บน 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 เป็นเครื่องมือที่ช่วยให้เราเห็นหน้าจอเว็บแอพพลิเคชันของเราในเบื้องต้นได้

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

  • สร้างโปรแกรมทดสอบเว็บแอพพลิเคชันอัตโนมัติด้วย Selenium WebDriver : ตอนที่ 1 การติดตั้ง Web Driver

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

    • Selenium Integrated Development Environment (Selenium IDE)
    • Selenium Remote Control (RC)
    • WebDriver
    • Selenium Grid

    SeleniumSuite

    โดยในปัจจุบัน ในส่วนของ Selenium RC (Selenium 1.0) และ WebDriver ได้ร่วมเป็น Selenium ในเวอร์นที่ 2 ซึ่งในบทความนี้ ผู้เขียนจะขอเรียกว่า Selenium Web Driver เพื่อไม่ให้สับสนกับ Selenium ค่ะ ต่อไปก็ขออธิบายลงไปที่ Selenium Web Driver ต่อไปเลยค่ะ 

    Selenium Web Driver เป็นเครื่องมือที่ช่วยให้เราสามารถสร้างโปรแกรมในการทดสอบเว็บแอพพลิเคชันกับ Web browser ได้หลายตัว ซึ่งถือเป็นคุณสมบัติเด่นที่ดีกว่า Selenium IDE ค่ะ (Selenium IDE จะใช้งานได้เฉพาะ firefox เท่านั้น) โดยจะมี Web Driver เป็นตัวกลางที่มีไลบารีที่ช่วยในเราติดต่อกับ Web browser ได้ดังรูปค่ะ

    WebDriver_and_Browsers(1)

    โดยใช้การเขียนโปรแกรมในภาษาต่างๆ ที่เราคุ้นเคยกัน ไม่ว่าจะเป็น JAVA, .Net (VB/C#), Ruby ติดต่อกับไลบารีของ WebDriver เพื่อเข้าถึงคอนโทรลที่แสดงผ่าน Web browser ได้ ทำให้เราสามารถสร้างโปรแกรมการทดสอบได้หลากหลายมากขึ้น เช่น

    • ดึงข้อมูลที่ใช้สำหรับกรอกข้อมูลบนฟอร์ม จากฐานข้อมูลได้
    • สามารถใช้ทดสอบหลายๆ กรณี ได้อย่างต่อเนื่อง

    เริ่มเห็นประโยชน์กันบ้างยังค่ะ ต่อไปเพื่อไม่ให้เป็นการเสียเวลาและเพื่อให้เห็นภาพมากยิ่งขึ้น เราก็มาเริ่มการติดตั้งเลย โดยบทความนี้จะใช้ WebDriver ของ Firefox และภาษา C# ในการพัฒนา

    ขั้นตอนที่ 1 : โหลด Selenium Client และ WebDriver
    ไปยัง URL : http://www.seleniumhq.org/download/
    Selenium Client คือ ไลบาลีที่ใช้ติดต่อกับ WebDriver ซึ่งขึ้นอยู่กับภาษาที่ใช้ในการพัฒนา

    11

    12

    (จากรูปด้านบน เลือกตามภาษาที่ใช้ในการพัฒนา)

    WebDriver คือ ไลบารีที่ใช้ติดต่อกับ Web brower ซึ่งในส่วนของ firefox จะติดมากับไลบารีของ Selenium Client อยู่แล้ว แต่ในส่วนของ IE , Chrome หรือ Safari ต้องโหลดแยกต่างหาก โดยในส่วนของเว็บ Selenium ก็มีให้โหลดเรียบร้อยแล้ว 

    13

    14

    (จากรูปบน เป็น Webdriver ของ Chrome เลือกตามระบบปฎิบัติการใช้งาน)

    15

    (จากรูปบน เป็นไฟล์ Webdriver ของ Chrome โดยเราจะเรียกใช้โดยการอ้างอิงจากพาร์ธ)

    ขั้นตอนที่ 2 : การติดตั้งไลบารีกับ Visual Studio

    เมื่อดาวน์โหลดมาเรียบร้อย ทำการแตกไฟล์จะได้โฟลเดอร์ตามรูป โดยเลือกใช้งานตาม .Net Framework ที่ใช้ในการพัฒนา

    16

    จากนั้นทำการสร้าง Project และทำการ Add Referrence

    1

    2

    ขั้นตอนที่ 3 : Hello World !!

    ขั้นตอนนี้ทำการเขียนโปรแกรม โดยการกดปุ่มจากฟอร์มและโปรแกรมทำการเปิดเว็บเพจ google ค้นหาคำว่า Hello World

    18

    3

    (จากรูปบน เป็นส่วนของการประกาศใช้งานไลบารีของ Selenium ซึ่งในกรณีนี้อยู่ที่ว่าจะใช้เรียกเว็บ browser อะไรก็เรียกใช้ไลบารีตัวดังกล่าว จากตัวอย่างผู้เขียนจะเรียกใช้ Firefox กับ Chrome)

    5

    (จากรูปบน เป็นการสร้างตัวแปรคลาส driver ของ Firefox)

    4

    (จากรูปบน เป็นการสร้างตัวแปรคลาส driver ของ Chrome โดยจะแตกต่างจากของ Firefox ตรงที่ต้องมีการระบุตำแหน่งของ driver ซึ่งในขั้นตอนที่ 2 ได้ทำการโหลดมา)

    6

    (จากรูปบน เป็นการเขียนโปรแกรมให้เว็บของ google ค้นหาว่า Hello World!!! คำอธิบายตาม comment เลยจร้า  โดยรูปแบบการค้นหายังมีอีกหลายวิธี ซึ่งอธิบายในตอนต่อไป  )

    เขียนเสร็จเรียบร้อย ลอง run ดูจะได้ผลลัพธ์ตามข้างล่าง ไป Hello World กัน !!! 

    17

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

    อ้างอิง :

    www.seleniumhq.org  
    www.guru99.com/selenium-tutorial.html

  • แจกฟรี Template สำหรับ Web Application แบบไทยๆ

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

    ส่วนของ source code ที่นำมาใช้และมีการปรับแต่ดังนี้ (จัดเต็ม..เวอร์ชั่นล่าสุด ณ 14/8/2558) (more…)

  • 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

  • @FONT-FACE

    การใช้ฟอนต์นำเสนอข้อมูลข่าวสารบนเว็บไซต์ หรือ Web Typography ในยุคแรก นั้น Browser จะเป็นตัวกำหนดว่าจะนำเสนอด้วยฟอนต์อะไร ทำให้การแสดงผลในแต่ละ Browser ไม่เหมือนกัน ต่อมาใน HTML2 ได้มีการเพิ่มแท็ก <font> เข้ามา และใน CSS2 ก็อนุญาตให้เราสามารถกำหนดฟอนต์ได้เอง แต่ปัญหาที่เจอคือฟอนต์ที่เราเลือกใช้จะต้องถูกติดตั้งบนเครื่องฝั่งผู้ใช้ด้วย ดังตัวอย่างการกำหนดรูปแบบ font ด้วย CSS2 ดังนี้

    body { font-family: Gill, Helvetica, sans-serif }

    เมื่อ Web Browser อ่านเจอ CSS ดังกล่าว อันดับแรกก็จะดูว่าฟอนต์ที่ชื่อ Gill ถูกติดตั้งไว้ในเครื่องแล้วหรือยัง ถ้าติดตั้งแล้วก็จะแสดงผลเว็บไซต์ด้วยฟอนต์ Gill แต่ถ้ายังไม่ได้ติดตั้ง Browser ก็จะมองหาฟอนต์ตัวถัดไปคือ Helvetica และ sans-serif ตามลำดับ

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

    อะไรๆ ก็ง่ายขึ้นด้วย @font-face

    ถือว่าเป็นข่าวดีของนักพัฒนาเว็บไซต์เป็นอย่างมาก เมื่อ CSS3 ได้กำหนด @font-face ไว้ใน specification ด้วย ทำให้การใช้ฟอนต์บนเว็บไซต์มีความหลากหลายมากยิ่งขึ้น

    @font-face เป็นกลไกที่จะช่วยให้เราสามารถเรียกใช้ฟอนต์ที่ไม่ได้ถูกติดตั้งไว้บนเครื่องฝั่งผู้ใช้ได้ โดยให้ระบุแหล่งที่อยู่ (src) ของไฟล์ฟอนต์ไว้ใน Style Sheet ดังนี้

    @font-face {

    font-family: myFirstFont;

    src: url(‘www.mydomain.com/sansation_light.woff’);

    }

    จากโค้ดเมื่อ Web Browser อ่านเจอก็จะไปโหลดฟอนต์ตามที่อยู่ที่ระบุไว้มาใช้งาน ทำให้เราสามารถกำหนดฟอนต์ได้ตามต้องการ

    ปัญหาต่อมาที่เจอคือแต่ละ Web Browser นั้นรองรับไฟล์ฟอนต์ในรูปแบบ (format) ที่แตกต่างกันออกไป ในโค้ดตัวอย่างใช้รูปแบบ Web Open Font Format (woff) รูปแบบอื่นที่มีการใช้งานกันได้แก่ EOT (Embedded OpenType), TrueType Font (ttf), Open Type Font (otf) และ Scalable Vector Graphics (SVG) เป็นต้น โดยแต่ละ Browser รองรับรูปแบบไฟล์ต่างกันดังรูป

    font-browser-supportที่มา : www.w3schools.com

    แล้วทีนี้เราจะเขียน CSS อย่างไรให้รองรับทุก Browser ล่ะ? คำตอบก็คือ ให้เราระบุแหล่งที่อยู่ของไฟล์ฟอนต์ (src) แต่ละรูปแบบต่อท้ายเข้าไปเลยดังนี้

    @font-face {

    font-family: myFirstFont;

    src: url(‘sansation_light.woff’);

    src: url(‘sansation_light.woff’) format(‘woff’),

    url(‘sansation_light.eot) format(‘embeded-opentype’),

    url(‘sansation_light.ttf) format(‘truetype);

    }

    จากโค้ดในส่วนของ format จะเป็นตัวบอก Browser ว่าไฟล์ที่ระบุตรงกับรูปแบบที่รองรับหรือไม่ ถ้าตรงก็โหลดมาใช้งาน แต่ถ้าไม่ตรงก็จะไม่โหลดลงมาให้เปลือง Banwidth

    แล้วจะหาไฟล์ฟอนต์ได้จากไหน?

    อ่านมาถึงตรงนี้แล้ว หลายคนอาจจะกังวลว่า แล้วจะหาไฟล์ woff, eot, ttf เหล่านี้ได้จากไหน? คำตอบคือให้ทำตามขั้นตอนดังนี้

    1. ดาวน์โหลดฟอนต์ในรูปแบบ ttf ที่ต้องการก่อนครับ แนะนำเว็บไซต์ f0nt.com จะมีฟอนต์ภาษาไทยสวยๆ ให้เลือกมากมาย และที่สำคัญคือสามารถใช้ได้ฟรี ไม่มีค่าลิขสิทธิ์
    2. เข้าเว็บไซต์ http://www.font2com/
    3. ให้อัพโหลดไฟล์ฟอนต์ที่เตรียมไว้ แล้วคลิกปุ่ม Convert & Download ได้เลยครับ จะได้เป็นไฟล์ zip มา ข้างในประกอบด้วยไฟล์ css, demo.html และไฟล์ฟอนต์ต่างๆ ให้เราเอาไปใช้งานได้เลยครับ

     

  • 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