ปัญหา:
บริษัทของฉันจัดทำจดหมายข่าวรายเดือนซึ่งฉันโฮสต์ไว้บนเว็บไซต์ภายในของเรา ฉันมีหน้าสำหรับผู้เขียนจดหมายข่าวเพื่ออัปโหลดเวอร์ชันล่าสุด เมื่อผู้เขียนได้อัปโหลดจดหมายข่าวล่าสุดแล้ว เขาจะส่งอีเมลออกอากาศเพื่อประกาศจดหมายข่าวฉบับใหม่ พนักงานจะตรวจสอบจดหมายข่าวใหม่อย่างสม่ำเสมอและส่งข้อเสนอแนะไปยังผู้เขียนพร้อมการแก้ไขที่จำเป็นต้องดำเนินการ
เมื่อผู้เขียนได้ทำการแก้ไขที่จำเป็นแล้ว (โดยทั่วไปภายในหนึ่งชั่วโมงหลังจากส่งอีเมลออกอากาศ) เขาจะกลับมาที่หน้าของฉันอีกครั้งและแทนที่เวอร์ชันล่าสุดด้วยจดหมายข่าวที่อัปเดต
ทันทีหลังจากการแทนที่ (หรืออัปเดต หากคุณต้องการ) จดหมายข่าว ใครก็ตามที่พยายามเข้าถึงจดหมายข่าวจะได้รับข้อผิดพลาด 500 - ข้อผิดพลาดเซิร์ฟเวอร์ภายใน
เจ้าหน้าที่ไอทีของฉันไม่สามารถลบ/เปลี่ยนชื่อ/ย้ายไฟล์ได้เนื่องจากข้อผิดพลาดในการอนุญาต และต้องทำสิ่งที่ซับซ้อนหลายอย่างเพื่อให้ไฟล์ถูกลบ (และเมื่อไฟล์ถูกลบแล้ว ผู้เขียนจดหมายข่าวสามารถ อัปโหลดสำเนาที่ถูกต้องและทำงานได้ดี
เจ้าหน้าที่ไอทีของฉันและฉันค่อนข้างแน่ใจว่าปัญหาเกิดจากการที่ฉันพยายามแทนที่ไฟล์ในขณะที่ IIS ให้บริการแก่ผู้ใช้อย่างแข็งขัน (ซึ่งฉันคิดและคิดว่าฉันได้เขียนโค้ดไว้ไม่ให้เกิดขึ้น)
รหัสที่เรียกใช้การแทนที่มีดังนี้:
Protected Sub ReplaceLatestNewsletter()
Dim dr As DataRow
Dim sFile As String
Dim mFileLock As Mutex
Try
If Me.Archives.Rows.Count > 0 Then
dr = Me.Archives.Rows(0)
sFile = dr("File").ToString
If dr("Path").ToString.Length > 0 Then
mFileLock = New Mutex(True, "MyMutexToPreventReadsOnOverwrite")
Try
mFileLock.WaitOne()
System.IO.File.Delete(dr("Path").ToString)
Catch ex As Exception
lblErrs.Text = ex.ToString
Finally
mFileLock.ReleaseMutex()
End Try
End If
fuNewsletter.PostedFile.SaveAs(Server.MapPath("~/Newsletter/archives/" & sFile))
End If
Catch ex As Exception
lblErrs.Text = ex.ToString
End Try
dr = Nothing
sFile = Nothing
mFileLock = Nothing
End Sub
ฉันคิดว่า Mutex
จะดูแลเรื่องนี้ (แม้ว่าหลังจากอ่านเอกสารซ้ำแล้ว ฉันไม่แน่ใจว่าจะสามารถใช้งานได้จริงเหมือนที่ฉันพยายาม) ความคิดเห็นอื่น ๆ เกี่ยวกับโค้ดด้านบน:
Me.Archives
เป็นDataTable
ที่เก็บไว้ในViewState
dr("File").ToString
คือชื่อไฟล์ (ไม่มีเส้นทาง)dr("Path").ToString
คือเส้นทางเครื่องแบบเต็มและชื่อไฟล์ (เช่น 'C:\App_Root\Newsletters\archives\20120214.pdf')- ชื่อไฟล์ของจดหมายข่าวถูกตั้งค่าเป็น "YYYYMMDD.pdf" โดย YYYYMMDD คือวันที่ (จัดรูปแบบ) ของการอัปโหลด
ไม่ว่าในกรณีใด ฉันค่อนข้างแน่ใจว่าโค้ดด้านบน ไม่ ทำการล็อกไฟล์แบบเอกสิทธิ์เฉพาะบุคคลเพื่อให้สามารถเขียนทับไฟล์ได้อย่างปลอดภัย
ท้ายที่สุด ฉันต้องการให้แน่ใจว่าสิ่งต่อไปนี้จะเกิดขึ้น:
- หาก IIS กำลังให้บริการไฟล์อยู่ ให้รอจนกว่า IIS จะให้บริการเสร็จสิ้น
- ก่อนที่ IIS จะสามารถให้บริการไฟล์ได้อีกครั้ง ให้สร้างการล็อคแบบพิเศษบนไฟล์ เพื่อไม่ให้กระบวนการอื่น เธรด ผู้ใช้ (ฯลฯ) สามารถอ่านหรือเขียนลงในไฟล์ได้
- ลบไฟล์ทั้งหมดและเขียนไฟล์ใหม่เพื่อแทนที่หรือเขียนทับไฟล์ที่มีอยู่ด้วยเนื้อหาใหม่
- ลบการล็อคแบบเอกสิทธิ์เฉพาะบุคคลเพื่อให้ผู้ใช้สามารถเข้าถึงไฟล์ได้อีกครั้ง
ข้อเสนอแนะ?
นอกจากนี้ ฉันสามารถใช้ Mutex
เพื่อล็อคไฟล์ในระบบไฟล์ Windows ได้หรือไม่
ขอขอบคุณล่วงหน้าสำหรับความช่วยเหลือและคำแนะนำของคุณ
แก้ไข:
วิธีสร้างลิงก์สำหรับจดหมายข่าวจะขึ้นอยู่กับชื่อไฟล์จริง วิธีการที่ใช้คือ:
- รับไฟล์ PDF ทั้งหมดในไดเร็กทอรี "archives" สำหรับแต่ละไฟล์:
- แยกวันที่เผยแพร่จากชื่อไฟล์
- เก็บวันที่ เส้นทางไปยังไฟล์ ชื่อไฟล์ และ URL ของแต่ละไฟล์ในรูปแบบ
DataRow
ในDataTable
- จัดเรียง
DataTable
ตามวันที่ (จากมากไปน้อย) - ส่งออกแถวแรกเป็นปัญหาปัจจุบัน
- ส่งออกแถวที่ตามมาทั้งหมดเป็น "ไฟล์เก็บถาวร" โดยจัดเรียงตามปีและเดือน
อัปเดต:
แทนที่จะไม่สามารถแยกแยะได้เมื่อคำขอที่มีอยู่ทั้งหมดสำหรับไฟล์นั้นเสร็จสมบูรณ์แล้ว ฉันจึงดูส่วนแรกของคำตอบของ @ Justin อย่างใกล้ชิดยิ่งขึ้น ("mutex ของคุณจะมีผลก็ต่อเมื่อกระบวนการที่อ่านจากไฟล์นั้นได้รับเช่นกัน mutex เดียวกัน")
สิ่งนี้ทำให้ฉัน กำหนดค่า IIS7 ให้กับเซิร์ฟเวอร์เนื้อหาคงที่ผ่าน ASP .NET Runtime และบทความที่เชื่อมโยงในคำตอบที่ยอมรับ
ด้วยเหตุนี้ ฉันจึงได้ใช้ตัวจัดการสำหรับไฟล์ PDF ทั้งหมดที่ใช้ New Mutex(True, "MyMutexToPreventReadsOnOverwrite")
เพื่อให้แน่ใจว่ามีเพียงเธรดเดียวเท่านั้นที่กำลังทำอะไรบางอย่างกับ PDF ในเวลาใดก็ตาม
ขอบคุณสำหรับคำตอบ @Justin แม้ว่าฉันจะไม่ได้ใช้การใช้งานที่คุณแนะนำ แต่คำตอบของคุณก็ชี้ให้ฉันไปสู่วิธีแก้ปัญหาที่ยอมรับได้