ฉันจะเขียนทับ / อัปเดตไฟล์ที่ IIS ให้บริการอยู่ในปัจจุบันได้อย่างไร

ปัญหา:

บริษัทของฉันจัดทำจดหมายข่าวรายเดือนซึ่งฉันโฮสต์ไว้บนเว็บไซต์ภายในของเรา ฉันมีหน้าสำหรับผู้เขียนจดหมายข่าวเพื่ออัปโหลดเวอร์ชันล่าสุด เมื่อผู้เขียนได้อัปโหลดจดหมายข่าวล่าสุดแล้ว เขาจะส่งอีเมลออกอากาศเพื่อประกาศจดหมายข่าวฉบับใหม่ พนักงานจะตรวจสอบจดหมายข่าวใหม่อย่างสม่ำเสมอและส่งข้อเสนอแนะไปยังผู้เขียนพร้อมการแก้ไขที่จำเป็นต้องดำเนินการ

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

ทันทีหลังจากการแทนที่ (หรืออัปเดต หากคุณต้องการ) จดหมายข่าว ใครก็ตามที่พยายามเข้าถึงจดหมายข่าวจะได้รับข้อผิดพลาด 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 คือวันที่ (จัดรูปแบบ) ของการอัปโหลด

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

ท้ายที่สุด ฉันต้องการให้แน่ใจว่าสิ่งต่อไปนี้จะเกิดขึ้น:

  1. หาก IIS กำลังให้บริการไฟล์อยู่ ให้รอจนกว่า IIS จะให้บริการเสร็จสิ้น
  2. ก่อนที่ IIS จะสามารถให้บริการไฟล์ได้อีกครั้ง ให้สร้างการล็อคแบบพิเศษบนไฟล์ เพื่อไม่ให้กระบวนการอื่น เธรด ผู้ใช้ (ฯลฯ) สามารถอ่านหรือเขียนลงในไฟล์ได้
  3. ลบไฟล์ทั้งหมดและเขียนไฟล์ใหม่เพื่อแทนที่หรือเขียนทับไฟล์ที่มีอยู่ด้วยเนื้อหาใหม่
  4. ลบการล็อคแบบเอกสิทธิ์เฉพาะบุคคลเพื่อให้ผู้ใช้สามารถเข้าถึงไฟล์ได้อีกครั้ง

ข้อเสนอแนะ?

นอกจากนี้ ฉันสามารถใช้ Mutex เพื่อล็อคไฟล์ในระบบไฟล์ Windows ได้หรือไม่

ขอขอบคุณล่วงหน้าสำหรับความช่วยเหลือและคำแนะนำของคุณ

แก้ไข:

วิธีสร้างลิงก์สำหรับจดหมายข่าวจะขึ้นอยู่กับชื่อไฟล์จริง วิธีการที่ใช้คือ:

  1. รับไฟล์ PDF ทั้งหมดในไดเร็กทอรี "archives" สำหรับแต่ละไฟล์:
  2. แยกวันที่เผยแพร่จากชื่อไฟล์
  3. เก็บวันที่ เส้นทางไปยังไฟล์ ชื่อไฟล์ และ URL ของแต่ละไฟล์ในรูปแบบ DataRow ใน DataTable
  4. จัดเรียง DataTable ตามวันที่ (จากมากไปน้อย)
  5. ส่งออกแถวแรกเป็นปัญหาปัจจุบัน
  6. ส่งออกแถวที่ตามมาทั้งหมดเป็น "ไฟล์เก็บถาวร" โดยจัดเรียงตามปีและเดือน

อัปเดต:

แทนที่จะไม่สามารถแยกแยะได้เมื่อคำขอที่มีอยู่ทั้งหมดสำหรับไฟล์นั้นเสร็จสมบูรณ์แล้ว ฉันจึงดูส่วนแรกของคำตอบของ @ Justin อย่างใกล้ชิดยิ่งขึ้น ("mutex ของคุณจะมีผลก็ต่อเมื่อกระบวนการที่อ่านจากไฟล์นั้นได้รับเช่นกัน mutex เดียวกัน")

สิ่งนี้ทำให้ฉัน กำหนดค่า IIS7 ให้กับเซิร์ฟเวอร์เนื้อหาคงที่ผ่าน ASP .NET Runtime และบทความที่เชื่อมโยงในคำตอบที่ยอมรับ

ด้วยเหตุนี้ ฉันจึงได้ใช้ตัวจัดการสำหรับไฟล์ PDF ทั้งหมดที่ใช้ New Mutex(True, "MyMutexToPreventReadsOnOverwrite") เพื่อให้แน่ใจว่ามีเพียงเธรดเดียวเท่านั้นที่กำลังทำอะไรบางอย่างกับ PDF ในเวลาใดก็ตาม

ขอบคุณสำหรับคำตอบ @Justin แม้ว่าฉันจะไม่ได้ใช้การใช้งานที่คุณแนะนำ แต่คำตอบของคุณก็ชี้ให้ฉันไปสู่วิธีแก้ปัญหาที่ยอมรับได้


person pete    schedule 21.02.2012    source แหล่งที่มา
comment
ฉันเพิ่มแท็ก c# เนื่องจากฉันสามารถอ่าน c# และแปลกลับเป็น vb.net รู้สึกอิสระที่จะโพสต์รหัสใน c #   -  person pete    schedule 21.02.2012
comment
ด้วยความเคารพ - นั่นมันโง่มาก ทำไมไม่เพียงแค่ 1) อัปโหลดไปยังพื้นที่การแสดงละคร (นั่นคือ เสมอ เขียนได้) จากนั้น 2) คัดลอก - โดยมีข้อผิดพลาดในการลองไฟล์อีกครั้งในการใช้งาน   -  person paulsm4    schedule 21.02.2012


คำตอบ (1)


mutex ของคุณจะมีผลก็ต่อเมื่อกระบวนการที่อ่านจากไฟล์ได้รับ mutex เดียวกันด้วย วิธีการที่ใช้ในการให้บริการไฟล์คืออะไร? มีการใช้ ASP.Net หรือเป็นเพียงไฟล์คงที่หรือไม่

ขั้นตอนการทำงานของฉันจะแตกต่างออกไปเล็กน้อย:

  1. เขียนจดหมายข่าวใหม่ลงในไฟล์ ใหม่
  2. ให้ IIS เริ่มให้บริการไฟล์ใหม่แทนที่จะเป็นไฟล์เก่าสำหรับ URL จดหมายข่าวที่กำหนด
  3. ลบไฟล์เก่าเมื่อคำขอที่มีอยู่ทั้งหมดสำหรับไฟล์นั้นเสร็จสิ้นแล้ว

ซึ่งไม่จำเป็นต้องล็อค และยังหมายความว่าเราไม่จำเป็นต้องรอให้คำขอสำหรับไฟล์ปัจจุบันเสร็จสมบูรณ์ (ซึ่งอาจใช้เวลานานอย่างไม่มีกำหนดหากผู้คนยังคงส่งคำขอใหม่ต่อไป) บิตที่น่าสนใจเพียงอย่างเดียวคือขั้นตอนที่ 2 ซึ่งจะขึ้นอยู่กับวิธีการให้บริการไฟล์ วิธีที่ง่ายที่สุดอาจเป็นการตั้งค่าการเปลี่ยนเส้นทาง HTTP หรือใช้การเขียน URL ใหม่

การเปลี่ยนเส้นทาง HTTP

การเปลี่ยนเส้นทาง HTTP คือจุดที่เซิร์ฟเวอร์บอกให้ไคลเอ็นต์ค้นหาตำแหน่งอื่นเมื่อได้รับการร้องขอทรัพยากรที่กำหนด เพื่อให้ URL ของเบราว์เซอร์ได้รับการอัปเดตโดยอัตโนมัติเพื่อให้ตรงกับตำแหน่งใหม่ ตัวอย่างเช่น หากผู้ใช้ร้องขอ http://server/20120221.pdf พวกเขาอาจถูกเปลี่ยนเส้นทางไปยัง URL อื่นโดยอัตโนมัติ เช่น http://server/20120221_v2.pdf (URL ที่แสดงในเบราว์เซอร์จะเปลี่ยน แต่ URL ที่พวกเขาจำเป็นต้องพิมพ์จะไม่เปลี่ยนแปลง)

คุณสามารถทำได้ใน IIS 7 โดยใช้องค์ประกอบการกำหนดค่า httpRedirect เช่น:

<configuration>
   <system.webServer>
      <httpRedirect enabled="true" exactDestination="true" httpResponseStatus="Found">
         <!-- Note that I needed to add a * in for IIS to accept the wildcard even though it isn't used in this case -->
         <add wildcard="*20120221.pdf" destination="20120221_v2.pdf" />
      </httpRedirect>
   </system.webServer>
</configuration>

หน้าที่เชื่อมโยงจะแสดงวิธีเปลี่ยนการตั้งค่าเหล่านี้จาก ASP.Net

การเขียน URL ใหม่

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

การใช้การเปลี่ยนเส้นทาง HTTP น่าจะเป็นวิธีที่ง่ายที่สุด

person Justin    schedule 21.02.2012
comment
ปัญหาของฉันเกี่ยวกับสิ่งนั้นเป็นสองเท่า 1) ฉันไม่รู้ว่าต้องทำอย่างไร 2) ชื่อไฟล์ต้องอยู่ในรูปแบบ YYYYMMDD.pdf ดังนั้นฉันจะเขียนไฟล์ ใหม่ ด้วยชื่อเดียวกันในขณะที่ยังคงให้บริการไฟล์เก่าได้อย่างไร หากคุณมีตัวอย่าง ฉันยินดีที่จะเรียนรู้ - person pete; 21.02.2012
comment
มีการใช้ ASP.Net หรือเป็นเพียงไฟล์คงที่หรือไม่ มันเป็นเพียงไฟล์คงที่ - person pete; 21.02.2012
comment
ตกลงดังนั้นเขียนลงในไฟล์ใหม่ เพิ่มการเปลี่ยนเส้นทางแบบไดนามิก สำหรับขั้นตอนที่ 3 ฉันจะทราบได้อย่างไรว่าคำขอที่มีอยู่ทั้งหมดสำหรับไฟล์นั้นเสร็จสมบูรณ์แล้ว - person pete; 21.02.2012
comment
@pete ฉันไม่แน่ใจว่าคุณทำได้ - ฉันจะปล่อยมันไว้ที่นั่นเพื่อลบเป็นกลุ่มในภายหลังหรือลองลบอีกครั้งในวงโดยมีการหน่วงเวลาสั้น ๆ - person Justin; 22.02.2012