การอัปโหลดไฟล์หลาย ๆ ไฟล์พร้อมกันด้วย Dojo

ที่มา

การ upload แฟ้มข้อมูล (File) เป็นกิจกรรมที่อยู่คู่กับ Web application มาเนิ่นนาน ยิ่งเมื่อการพัฒนา Web application เข้าสู่ยุคสมัยของ Web 2.0 และเป็น Web 3.0 ในยุคปัจจุบันก็ยิ่งเกิดเครื่องมือต่าง ๆ เพื่ออำนวยความสะดวกในการ upload ไฟล์มากขึ้นรวมไปถึงความสามารถในการอัปโหลดหลาย ๆ ไฟล์พร้อมกัน (Multi-File Uploading)

ณ เวลานี้มีเครื่องมือมากมายสำหรับนักพัฒนา Web application  ผู้อ่านอาจมีโอกาสได้ใช้งานเครื่องมือจากบางค่ายบ้างแล้ว แต่ในบทความนี้ผู้เขียนจะแนะนำวิธีการใช้งานเครื่องมือในการทำให้ Web application ของเราสามารถรองรับความต้องการในการอัปโหลดไฟล์หลาย ๆ ไฟล์ได้พร้อมกันตัวหนึ่งชื่อ HTML5 Multi-File Uploader ของ Dojo Toolkit ซึ่งทำงานได้ตามความต้องการของผู้เขียนดังนี้

ความต้องการ

  1. สามารถ upload file ได้หลาย ๆ ไฟล์พร้อมกัน
  2. upload file ด้วย AJAX
  3. ต้องไม่มีการ Post Back หรือทำให้เกิดการ Load หน้า Web ใหม่
  4. สามารถปรับแต่งได้ตามความต้องการ

ที่จริงแล้วเครื่องมือของ Dojo Toolkit เป็นเครื่องมือที่ใช้ยากพอสมควรสำหรับคนที่ไม่ค่อยถนัดในการใช้งาน JavaScript แต่เนื่องจาก API ที่เปิดโอกาสให้ผู้ใช้ได้เข้าถึงทุกส่วนของ Widget ทำให้เราสามารถปรับแต่งการใช้งานเพื่อให้เหมาะกับความต้องการได้โดยอิสระ

ศึกษาคุณสมบัติของเครื่องมือ

HTML5 Multi-File Uploader เป็นส่วนหนึ่งใน package ชื่อ dojox (dojox/form/Uploader) ซึ่งในความเป็นจริงแล้ว เครื่องมือตัวนี้ไม่ได้เจาะจงเฉพาะการใช้งาน HTML5 เท่านั้นแต่ยังคงใช้งานกับ Flash หรือ iframe ได้ด้วยตามแต่ผู้ใช้จะปรับแต่งเนื่องจาก Web browser แต่ละยี่ห้อก็มีข้อจำกัดแตกต่างกันไปตาม Browser engine และ Rendering engine

ตามชื่อของเครื่องมือ Uploader ใช้ input ของ HTML5 เป็นองค์ประกอบหลัก จึงต้องกำหนดใน Form ให้รองรับการอัปโหลดไฟล์ด้วยการกำหนด attribute ชื่อ method และ enctype ดังนี้

 

<form method=”post” enctype=”multipart/form-data”> 

 

การสร้าง Widget ของ Uploader ทำได้ 2 รูปแบบดังนี้

Programmatically

require([

“dojox/form/Uploader”

], function (Uploader) {

myUploader = new dojox.form.Uploader();

}

 

Markup

<input name=”uploadedFile” multiple=”true” type=”file” data-dojo-type=”dojox/form/Uploader” data-dojo-props=”label: ‘Select Some Files’,……..” />

<div id=”files” data-dojo-type=”dojox/form/uploader/FileList” data-dojo-props=”uploaderId: ‘uploader'”></div>

ลองใช้งาน

เนื่องจากผู้เขียนพัฒนา Web application ด้วย Visual C# จึงจะยกตัวอย่างการประยุกต์การใช้งาน Uploader ร่วมกับ ASP.NET ซึ่งมีขั้นตอนดังนี้

1. นำ Widget มาวางในส่วนที่เราต้องการ

<input name=”uploadedfile” type=”file” id=”uploader” data-dojo-id=”fileUploader” data-dojo-type=”dojox/form/Uploader” data-dojo-props=”label: ‘Select files’, url: ‘../Test/TestUpload.ashx’, multiple: true” />
<input type=”hidden” name=”hdnMasterId” id=”hdnMasterId” value=”test” />
<input type=”hidden” name=”hdnFileDesc” id=”hdnFileDesc” value=”test” />
<div id=”files” data-dojo-type=”dojox/form/uploader/FileList” data-dojo-props=”uploaderId: ‘uploader'”></div>

 

เมื่อสังเกตุแล้วจะเห็นได้ว่าจะไม่มีปุ่ม Submit เนื่องจากผู้เขียนต้องการ upload file แบบ AJAX จึงไม่ต้องการให้ผู้ใช้กด ENTER แล้วส่งฟอร์มทั้งหมดไปทันที อีกจุดหนึ่งคือผู้เขียนต้องการส่งข้อมูลบางอย่างไปในคราวเดียวกันด้วยคือ  hdnMasterId และ hdnFileDesc

 

ลอง Run ดูน่าจะได้น่าตาประมาณนี้

ex1

เมื่อเราลองเลือก file แล้ว ชื่อ file ที่เราเลือกจะแสดงอยู่ตำแหน่งด้านล่าง

ex2

2. กำหนดการทำงานในฝั่ง client

ในขั้นตอนนี้เราจะต้องเขียน JavaScript เพื่อควบคุมการทำงานของ widget โดยผู้เขียนจะกำหนดให้ทำการ upload file แบบ AJAX เมื่อกดปุ่มใดปุ่มหนึ่งดังนี้

fileUploader..set(“onComplete”, function(){

//อะไรก็ตามที่ต้องให้ทำเมื่อทำการ upload เสร็จแล้ว

});

fileUploader.submit();

 

3. กำหนดการทำงานในฝั่ง server

ในส่วนของฝั่ง server เราก็ต้องมีการกำหนดการทำงานเช่นกัน เช่น การจัดเก็บไฟล์ การดึงข้อมูลจากการส่งข้อมูลแบบ AJAX เนื่องจากเรามีการส่งข้อมูลอื่น ๆ ไปด้วย ทั้งนี้เราไม่จะเป็นจะต้องใช้ web form มารองรับในการ upload เราสามารถใช้ ashx มาทำงานแทนที่ได้เลย การเขียนโปรแกรมฝั่ง server เป็นดังตัวอย่างด้านล่าง

public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = “text/plain”;
JavaScriptSerializer js = new JavaScriptSerializer();
string result = String.Empty;

IDictionary<string, string> postData = new Dictionary<string, string>();

foreach (string name in context.Request.Form.Keys)
{
//context.Response.Write(“<br>KEYS: ” + name);
postData[name] = context.Request.Form[name];
}

//result = js.Serialize(new Response(false, postData[“hdnTest”], String.Empty));
//context.Response.Write(result);

string masterId = postData[“hdnMasterId”];
string description = postData[“hdnFileDesc”];

try
{
int len = context.Request.Files.Count;

for (int i = 0; i < len; i++)
{
HttpPostedFile postedFile = context.Request.Files.Get(i) as HttpPostedFile;

string[] fileNames = postedFile.FileName.Split(‘\\’);
string fileName = String.Empty;

if (fileNames.Length > 1)
{
fileName = fileNames[fileNames.Length – 1];
}
else
{
fileName = fileNames[0];
}

string[] fileType = postedFile.FileName.Split(‘.’);

string uploadPath = context.Server.MapPath(Settings.Default.UploadFilePath);
string newFileName = “[” + masterId + “]_” + fileName;
string saveFileName = uploadPath + @”\” + newFileName;
postedFile.SaveAs(saveFileName);

OutboundMaster app = OutboundMaster.GetById(masterId);
app.AddAttachedFile(newFileName, description);

result = js.Serialize(new BizResponse(true));
}

context.Response.Write(result);
}
catch (Exception ex)
{
result = js.Serialize(new BizResponse(false, ex.Message, ex.StackTrace));
context.Response.Write(result);
}
}

จะเห็นได้ว่าตัวแปรที่มาจากการ post จะอยู่ในรูปแบบของ array ดังนั้นเราจะต้องทำการหาตัวแปรด้วย key ก่อน จากนั้นก็จะเข้าสู่ขั้นตอนการบันทึกไฟล์ตามที่เราออกแบบ

ทั้งนี้ตัว Uploader มีข้อกำหนดข้อหนึ่งคือจะต้องมีการตอบสนองจากฝั่ง server หากไม่มีจะทำให้ Uploader แสดง error ถึงแม้ว่าการ upload file นั้นเสร็จสิ้นไปแล้วก็ตาม ดังนั้นที่ไฟล์ ashx จึงต้องมีการส่งข้อมูลกลับมาดังตัวอย่างด้านล่างนี้

context.Response.Write(……);

 

บทสรุป

การใช้งาน Uploader อาจจะซับซ้อนมากน้อยขึ้นอยู่กับคนที่นำไปใช้ และถึงแม้ว่าเครื่องมือตัวนี้จะไม่ค่อย compatibility กับ Visual Studio เนื่องจากแนวคิดในการทำงานนั้นแตกต่างกัน แต่ยังคงสามารถนำมาประยุกต์ใช้งานเพื่อให้ได้ผลลัพธ์ตามที่ต้องการได้ และเนื่องจาก Dojo Toolkit นั้นเปิดเผย source code ทำให้เราสามารถทำความเข้าใจและจุดประกายแนวคิดใหม่ ๆ ได้อีกด้วย