Как я могу перезаписать / обновить файл, который в настоящее время обслуживается IIS? - PullRequest
2 голосов
/ 21 февраля 2012

Проблема:

Моя компания выпускает ежемесячный информационный бюллетень, который я размещаю на нашем внутреннем веб-сайте.У меня есть страница для автора рассылки, чтобы загрузить последнюю версию.После того, как автор загрузил последнюю новостную рассылку, он отправляет широковещательное электронное письмо, чтобы объявить о новой рассылке.Сотрудники неизменно проверяют новую рассылку и отправляют автору отзыв с исправлениями, которые необходимо внести.

После того, как автор внес необходимые исправления (обычно в течение часа после отправки широковещательного электронного письма), он повторно посещает мою страницуи заменяет последнюю версию обновленным информационным бюллетенем.

Сразу после замены (или обновления, если хотите) информационного бюллетеня любой, кто пытается получить к нему доступ, получает 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", гдеГГГГММДД - это дата (отформатированная) загрузки.

В любом случае, я почти уверен, что приведенный выше код не устанавливает эксклюзивную блокировку файла так, чтобыфайл можно безопасно перезаписать.

В конечном счете, я хотел бы убедиться, что произойдет следующее:

  1. Если IIS в настоящее время обслуживает файл, подождите, пока IIS не закончит его обслуживание.
  2. Прежде чем IIS сможет снова обслуживать файл, установите исключительную блокировку для файла, чтобы никакой другой процесс, поток, пользователь (и т. Д.) Не могли читать или записывать в файл.
  3. Либо удалите файл полностью и напишите новый файл, чтобы заменить его, либо перезапишите существующийфайл с новым содержимым.
  4. Снимите эксклюзивную блокировку, чтобы пользователи могли снова получить доступ к файлу.

Предложения?

Кроме того, можно ли использовать Mutex чтобы получить взаимоисключающую блокировку файла в файловой системе Windows?

Заранее благодарим вас за помощь и советы.

РЕДАКТИРОВАТЬ:

Способ создания ссылок на новостную рассылку основан на физическом имени файла.Используемый метод:

  1. Получить все файлы PDF в каталоге «архивы».Для каждого файла:
  2. Анализ даты публикации по имени файла.
  3. Сохранение даты, пути к файлу, имени файла и URL-адреса каждого файла в DataRow вa DataTable
  4. Сортировка DataTable по дате (по убыванию).
  5. Вывести первую строку как текущий выпуск.
  6. Вывести все последующие строки как «архивы»по годам и месяцам.

ОБНОВЛЕНИЕ :

Вместо того, чтобы не различить, когда все существующие запросы для этого файла выполнены, я взялПрисмотритесь к первой части ответа @ Джастина («Ваш мьютекс будет иметь эффект, только если процесс, который читает из файла, также получит тот же мьютекс».)

Это привело меня к Настройка IIS7серверное статическое содержимое через ASP.NET Runtime и связанную статью в принятом ответе.

С этой целью я реализовал обработчик для всех файлов PDF, который реализует New Mutex(True, "MyMutexToPreventReadsOnOverwrite"), чтобы гарантировать, что только один поток что-то делает с PDF в любой момент времени.

Спасибо за ответ, @ Джастин. Хотя я не использовал предложенную вами реализацию, ваш ответ указал мне на приемлемое решение.

1 Ответ

3 голосов
/ 21 февраля 2012

Ваш мьютекс будет иметь эффект, только если процесс, который читает из файла, также получит тот же мьютекс.Какой метод используется для подачи файла?Используется ли ASP.Net или это просто статический файл?

Мой рабочий процесс будет немного другим:

  1. Записать новый бюллетень в новый файл
  2. Пусть IIS начнет обслуживать новый файл вместо старого для данного URL-адреса бюллетеня
  3. Удалить старый файл после завершения всех существующих запросов для этого файла

Это не требует блокировки, а также означает, что нам не нужно ждать выполнения запросов на текущий файл (что может потенциально занять неопределенное количество времени, если люди продолжают делать новые запросы).Единственный интересный бит - это шаг 2, который будет зависеть от того, как файл будет обслуживаться - возможно, проще всего было бы настроить перенаправление HTTP или использовать перезапись URL

HTTP Redirect

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 с использованием чего-то вроде this , однако для работы в IIS необходимо установить дополнительные компоненты.

Использование перенаправления HTTP, вероятно, самый простой метод.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...