ที่มา: Baoyu BLOG
1. ฉันจะอธิบายหลักการของการอัปโหลดแบบไม่มีส่วนประกอบทีละนิดพร้อมตัวอย่าง HTML ของไคลเอ็นต์มีดังนี้ หากต้องการเรียกดูไฟล์แนบที่อัปโหลด เราจะส่งองค์ประกอบ <input type="file"> แต่ต้องแน่ใจว่าได้ตั้งค่าแอตทริบิวต์ enctype ของแบบฟอร์มเป็น "multipart/form-data":
<form method="post" action="upload.asp" enctype="multipart/form-data">
<ฉลาก>
<ประเภทอินพุต = "ไฟล์" ชื่อ = "file1" />
</ฉลาก>
<br />
<input type="text" name="filename" value="default filename"/>
<br />
<ประเภทอินพุต = "ส่ง" ค่า = "ส่ง"/>
<ประเภทอินพุต = "รีเซ็ต" ค่า = "รีเซ็ต"/>
</แบบฟอร์ม>
ในโปรแกรม asp พื้นหลัง เคยเป็นเรื่องง่ายมากในการรับข้อมูล ASCII ที่ส่งมาจากแบบฟอร์ม แต่ถ้าคุณต้องการไฟล์ที่อัพโหลด คุณต้องใช้วิธี BinaryRead ของอ็อบเจ็กต์ Request เพื่ออ่านไฟล์นั้น วิธี BinaryRead ดำเนินการอ่านไบนารีของจำนวนไบต์ที่ระบุจากสตรีมอินพุตปัจจุบัน สิ่งหนึ่งที่ควรทราบก็คือ เมื่อใช้วิธี BinaryRead แล้ว คอลเลกชัน Request.Form หรือ Request.QueryString จะไม่สามารถใช้งานได้อีกต่อไป เมื่อรวมกับคุณสมบัติ TotalBytes ของออบเจ็กต์ Request ข้อมูลทั้งหมดที่ส่งโดยแบบฟอร์มสามารถแปลงเป็นไบนารี่ได้ แต่ข้อมูลเหล่านี้จะถูกเข้ารหัสทั้งหมด ขั้นแรก มาดูกันว่าข้อมูลเหล่านี้ถูกเข้ารหัสอย่างไร มีกฎใดบ้างที่ต้องปฏิบัติตาม ในโค้ด เราจะแปลงไบนารีที่อ่านโดย BinaryRead เป็นข้อความและส่งออกเป็นไฟล์ upload.asp ในเบื้องหลัง (หมายเหตุ อย่าอัปโหลดไฟล์ขนาดใหญ่ในตัวอย่างนี้ ไม่เช่นนั้นเบราว์เซอร์อาจขัดข้อง):
-
หรี่ biData, PostData
ขนาด = คำขอ TotalBytes
biData = Request.BinaryRead (ขนาด)
PostData = BinaryToString (biData, ขนาด)
Response.Write "<pre>" & PostData & "</pre>" 'ใช้รูปแบบล่วงหน้าและเอาต์พุตตามที่เป็นอยู่
'แปลงสตรีมไบนารี่เป็นข้อความด้วยความช่วยเหลือของ RecordSet
ฟังก์ชั่น BinaryToString (biData, ขนาด)
ค่าคงที่ adLongVarChar = 201
ตั้งค่า RS = CreateObject("ADODB.Recordset")
RS.Fields.Append "mBinary", adLongVarChar, ขนาด
RS.เปิด
RS.AddNew
RS("mBinary").ผนวกChunk(biData)
RS.อัพเดต
BinaryToString = RS("mBinary").ค่า
RS.ปิด
ฟังก์ชันสิ้นสุด
-
เพื่อความเรียบง่าย ให้อัปโหลดไฟล์ข้อความที่ง่ายที่สุด (G:homepage.txt โดยมีเนื้อหา "Baoyu: http://www.webuc.net ") เพื่อทดสอบ เก็บค่าเริ่มต้น "ชื่อไฟล์เริ่มต้น" ไว้ใน ชื่อไฟล์กล่องข้อความ ส่งและดูผลลัพธ์:
--------------------------------7d429871607fe
การจัดการเนื้อหา: form-data; name="file1"; filename="G:homepage.txt"
ประเภทเนื้อหา: ข้อความ/ธรรมดา
เป่าหยู: http://www.webuc.net
-------------------------------7d429871607fe
การจัดการเนื้อหา: แบบฟอร์มข้อมูล; ชื่อ = "ชื่อไฟล์"
ชื่อไฟล์เริ่มต้น
---------------------------------7d429871607fe--
จะเห็นได้ว่าสำหรับรายการในรูปแบบ "----- --------------------------7d429871607fe" ขอบเขตดังกล่าวใช้แยกชิ้นออกเป็นชิ้นๆ และมีข้อมูลคำอธิบายบางส่วนอยู่ที่ตอนต้นของแต่ละชิ้น ตัวอย่างเช่น: Content- Disposition: form-data; name="filename" ในข้อมูลคำอธิบาย คุณสามารถทราบชื่อของรายการแบบฟอร์มผ่าน name="filename" หากมีเนื้อหาเช่น filename="G:homepage.txt" หมายความว่าเป็นไฟล์ที่อัปโหลด หากเป็นไฟล์ที่อัปโหลด ข้อมูลคำอธิบายจะมีอีกหนึ่งบรรทัด Content-Type: text/plain เพื่ออธิบาย ประเภทเนื้อหาของไฟล์ . ข้อมูลคำอธิบายและข้อมูลเนื้อหาจะคั่นด้วยการขึ้นบรรทัดใหม่
โดยพื้นฐานแล้วมันชัดเจน ตามกฎนี้ เรารู้วิธีแยกข้อมูลและประมวลผลข้อมูลที่แยกจากกัน อย่างไรก็ตาม เราเกือบจะมองข้ามปัญหาหนึ่งไปซึ่งก็คือค่าขอบเขต ("-------" ข้างต้น) ตัวอย่าง) -----------------------7d429871607fe") รู้ได้อย่างไร ค่าขอบเขตนี้จะแตกต่างออกไปทุกครั้งที่คุณอัปโหลด โชคดีที่สามารถรับได้ผ่าน Request.ServerVariables("HTTP_CONTENT_TYPE") ใน asp. ตัวอย่างเช่น ในตัวอย่างข้างต้น เนื้อหาของ HTTP_CONTENT_TYPE คือ: "multipart/form-data; boundary=-- --------------------------7d429871607fe" ด้วยสิ่งนี้ เราไม่เพียงแต่สามารถระบุได้ว่า enctype="multipart/form- data " (หากไม่ได้ใช้ก็ไม่จำเป็นต้องดำเนินการด้านล่าง) คุณยังสามารถรับขอบเขตค่าขอบเขต=---------------------- ----- 7d429871607fe. (หมายเหตุ: ค่าขอบเขตที่ได้รับที่นี่มี "--" ที่จุดเริ่มต้นน้อยกว่าค่าขอบเขตด้านบน ควรบวกเพิ่ม)
ส่วนขั้นตอนการวิเคราะห์ข้อมูลนั้นจะไม่ลงรายละเอียดนะครับ ไม่มีอะไรมากไปกว่าการใช้ฟังก์ชันเช่น InStr และ Mid เพื่อแยกข้อมูลที่เราต้องการ
2. เมื่ออัปโหลดเป็นกลุ่ม ความคืบหน้าในการบันทึกควรสะท้อนถึงแถบความคืบหน้าแบบเรียลไทม์ สิ่งสำคัญคือต้องทราบว่าเซิร์ฟเวอร์ปัจจุบันได้รับข้อมูลแบบเรียลไทม์เท่าใด เมื่อคิดย้อนกลับไปถึงขั้นตอนการอัปโหลด เราดำเนินการผ่าน Request.BinaryRead(Request.TotalBytes) ในระหว่างกระบวนการ Request เราไม่สามารถทราบได้ว่าเซิร์ฟเวอร์ปัจจุบันได้รับข้อมูลไปเท่าใด ดังนั้นเราจึงทำได้เพียงวิธีแก้ปัญหาเท่านั้น หากเราสามารถแบ่งข้อมูลที่ได้รับออกเป็นส่วนๆ แล้วพิจารณาจากจำนวนบล็อกที่อัปโหลด เราก็สามารถคำนวณได้ว่าข้อมูลนั้นใหญ่แค่ไหนที่อัปโหลดในปัจจุบัน! กล่าวคือ หาก 1K คือ 1 บล็อก สตรีมอินพุตของการอัปโหลด 1MB จะถูกแบ่งออกเป็น 1,024 บล็อกเพื่อรับ ตัวอย่างเช่น หากฉันได้รับ 100 บล็อก หมายความว่าอัปโหลด 100K แล้ว เมื่อฉันเสนอการบล็อก หลายคนพบว่ามันเหลือเชื่อเพราะพวกเขาเพิกเฉยว่าวิธี BinaryRead ไม่เพียงแต่สามารถอ่านขนาดที่ระบุเท่านั้น แต่ยังอ่านอย่างต่อเนื่องอีกด้วย
เขียนตัวอย่างเพื่อตรวจสอบความสมบูรณ์ของการอ่านบล็อกตามตัวอย่างในตอนนี้ (โปรดทราบว่าตัวอย่างนี้ไม่ได้อัปโหลดไฟล์ขนาดใหญ่ ไม่เช่นนั้นอาจทำให้เบราว์เซอร์ขัดข้อง):
<%
Dim biData, PostData, TotalBytes, ChunkBytes
ChunkBytes = 1 * 1024 ' ขนาดก้อนคือ 1K
TotalBytes = Request.TotalBytes 'ขนาดรวม'
PostData = "" ' ข้อมูลแปลงเป็นประเภทข้อความ
ReadedBytes = 0 'เริ่มต้นเป็น 0
'อ่านเป็นชิ้นๆ.
ทำในขณะที่ ReadedBytes < TotalBytes
biData = Request.BinaryRead(ChunkBytes) 'ก้อนปัจจุบัน
PostData = PostData & BinaryToString(biData,ChunkBytes) 'แปลงส่วนปัจจุบันเป็นข้อความและประกบกัน
ReadedBytes = ReadedBytes + ChunkBytes 'บันทึกขนาดการอ่าน
ถ้า ReadedBytes > TotalBytes แล้ว ReadedBytes = TotalBytes
วนซ้ำ
Response.Write "<pre>" & PostData & "</pre>" ' ใช้รูปแบบล่วงหน้าและเอาต์พุตตามที่เป็นอยู่
' แปลงไบนารีสตรีมเป็นข้อความ
ฟังก์ชั่น BinaryToString (biData, ขนาด)
ค่าคงที่ adLongVarChar = 201
ตั้งค่า RS = CreateObject("ADODB.Recordset")
RS.Fields.Append "mBinary", adLongVarChar, ขนาด
RS.เปิด
RS.AddNew
RS("mBinary").ผนวกChunk(biData)
RS.อัพเดต
BinaryToString = RS("mBinary").ค่า
RS.ปิด
ฟังก์ชันสิ้นสุด
%>
ลองอัปโหลดไฟล์ข้อความตอนนี้ ผลลัพธ์ที่ได้พิสูจน์ว่าเนื้อหาที่อ่านในบล็อกเสร็จสมบูรณ์ และใน While loop เราสามารถบันทึกสถานะปัจจุบันลงใน Application ทุกครั้งที่วนซ้ำ จากนั้นเราสามารถผ่าน Access แอปพลิเคชันเพื่อรับแถบความคืบหน้าการอัปโหลดแบบไดนามิก
นอกจากนี้: ในตัวอย่างข้างต้น มีการใช้การประกบสตริง หากคุณต้องการประกบข้อมูลไบนารี คุณสามารถใช้เมธอด Write ของออบเจ็กต์ ADODB.Stream ได้ดังนี้:
Set bSourceData = createobject("ADODB.Stream" )
bSourceData.Open
bSourceData.Type = 1 'ไบนารี่
ทำในขณะที่ ReadedBytes < TotalBytes
biData = Request.BinaryRead(ChunkBytes)
bSourceData.Write biData ' ใช้วิธีการเขียนโดยตรงเพื่อเขียนสตรีมไฟล์ปัจจุบันลงใน bSourceData
ReadedBytes = ReadedBytes + ChunkBytes
ถ้า ReadedBytes > TotalBytes แล้ว ReadedBytes = TotalBytes
แอปพลิเคชัน ("ReadedBytes") = ReadedBytes
วนซ้ำ
3. บันทึกไฟล์ที่อัพโหลด รับข้อมูลที่ส่งผ่าน Request.BinaryRead หลังจากแยกไฟล์ที่อัพโหลดแล้ว วิธีการบันทึกจะแตกต่างกันไปขึ้นอยู่กับประเภทข้อมูล:
สำหรับข้อมูลไบนารี คุณสามารถบันทึกสตรีมไบนารีได้โดยตรงผ่านวิธีการ SaveToFile ของ วัตถุ ADODB.Stream กลายเป็นไฟล์
สำหรับข้อมูลข้อความ คุณสามารถบันทึกข้อมูลข้อความลงในไฟล์โดยใช้วิธีเขียนของออบเจ็กต์ TextStream
ข้อมูลข้อความและข้อมูลไบนารีสามารถแปลงเป็นข้อมูลอื่นได้อย่างง่ายดาย สำหรับการอัปโหลดไฟล์ขนาดเล็ก โดยทั่วไปแล้วไม่มีความแตกต่างระหว่างทั้งสอง อย่างไรก็ตาม ยังคงมีความแตกต่างบางประการเมื่อบันทึกระหว่างสองวิธี สำหรับออบเจ็กต์ ADODB.Stream จะต้องโหลดข้อมูลทั้งหมดก่อนจึงจะสามารถบันทึกเป็นไฟล์ได้ ดังนั้น การอัปโหลดไฟล์ขนาดใหญ่โดยใช้วิธีนี้จะใช้หน่วยความจำจำนวนมาก และสำหรับออบเจ็กต์ TextStream หลังจากสร้างไฟล์แล้ว คุณสามารถเขียนทีละส่วนและเขียนได้หลายครั้ง ข้อดีคือ จะไม่กินพื้นที่หน่วยความจำของเซิร์ฟเวอร์ รวมกับหลักการรับข้อมูลเข้า จากการวิเคราะห์บล็อกข้างต้น เราสามารถเขียนข้อมูลที่อัพโหลดแต่ละชิ้นลงตรงกลางไฟล์ได้ ฉันเคยทำการทดลองและอัปโหลดไฟล์มากกว่า 200 MB ไปยังเครื่องเดียวกัน เมื่อใช้วิธีแรก หน่วยความจำก็เพิ่มขึ้นเรื่อยๆ ท้ายที่สุดก็แจ้งโดยตรงว่าหน่วยความจำเสมือนของคอมพิวเตอร์ไม่เพียงพอ แม้ว่าแถบความคืบหน้าจะระบุว่าไฟล์ถูกอัพโหลดแล้ว แต่สุดท้ายไฟล์ก็ยังไม่ถูกบันทึก เมื่อใช้วิธีการหลัง โดยทั่วไปแล้วจะไม่มีการเปลี่ยนแปลงในหน่วยความจำระหว่างกระบวนการอัปโหลด
4. ปัญหาที่ยังไม่ได้รับการแก้ไข ฉันเห็น Bestcomy ในบล็อกอธิบายว่าองค์ประกอบการอัปโหลด Asp.Net ของเขาสามารถเป็นอิสระจาก Sever.SetTimeOut แต่ใน Asp ฉันไม่สามารถทำได้ สำหรับการอัปโหลดไฟล์ขนาดใหญ่ ฉันทำได้เพียง Server.SetTimeOut เท่านั้น จะถูกตั้งค่าให้มีขนาดใหญ่มาก ฉันไม่รู้ว่ามีวิธีแก้ปัญหาที่ดีกว่าหรือไม่
หากเราใช้วิธีการเขียนของออบเจ็กต์ TextStream เมื่อบันทึกไฟล์ หากผู้ใช้ขัดจังหวะการถ่ายโอนไฟล์เมื่อทำการอัพโหลด ส่วนของไฟล์ที่อัพโหลดจะยังคงอยู่ตรงนั้น จะดีมากหากสามารถดำเนินการอัพโหลดต่อได้ ปัญหาสำคัญคือ แม้ว่าเมธอด Request.BinaryRead จะสามารถอ่านเป็นบล็อกได้ แต่ก็ไม่สามารถข้ามการอ่านบางส่วนได้!
5. สรุป โดยพื้นฐานแล้วอธิบายไว้อย่างชัดเจน แต่โค้ดจริงมีความซับซ้อนกว่านี้มาก มีหลายประเด็นที่ต้องพิจารณา ส่วนที่ลำบากที่สุดคือการวิเคราะห์ข้อมูลแต่ละชิ้น วิเคราะห์ว่าเป็นของข้อมูลคำอธิบายหรือไม่ รายการแบบฟอร์มยังคงเป็นไฟล์ที่อัปโหลดและมีการอัปโหลดไฟล์หรือไม่...
ฉันเชื่อว่าจากคำอธิบายข้างต้น คุณสามารถพัฒนาองค์ประกอบการอัปโหลดแบบไม่มีส่วนประกอบที่มีประสิทธิภาพของคุณเองได้ ฉันคิดว่าผู้คนจำนวนมากขึ้นสนใจแค่โค้ดและไม่รู้วิธีเขียนเอง บางทีพวกเขาอาจไม่มีเวลา บางทีระดับอาจไม่เพียงพอ และอีกมากก็กลายเป็นนิสัย... ฉันมี เห็นเทคโนโลยีมากเกินไปในเรียงความแปดส่วน CSDN - ย่อหน้าคำอธิบายแล้วโค้ดทั้งหมด การสอนคนตกปลาไม่ดีเท่าการสอนเขาตกปลา ถ้าฉันให้รหัสคุณ คุณอาจไม่คิดว่าทำไมและนำไปใช้ในครั้งต่อไปเมื่อคุณประสบปัญหาคล้าย ๆ กัน คุณยังไม่รู้ว่าทำไม ฉันหวังว่าบทความนี้จะช่วยให้ผู้คนเรียนรู้บางสิ่งบางอย่างได้มากขึ้น และสิ่งสำคัญที่สุดคือการ "ให้ความกระจ่าง" บางสิ่งบางอย่าง!