Response.TransmitFile () с общим ресурсом UNC (ASP.NET) - PullRequest
2 голосов
/ 31 октября 2009

В комментариях к этой странице:

http://msdn.microsoft.com/en-us/library/12s31dhy%28v=VS.90%29.aspx

.. это говорит о том, что TransmitFile () нельзя использовать с общими ресурсами UNC. Насколько я могу судить, это так; Я получаю эту ошибку в журнале событий при попытке:

TransmitFile failed. File Name: \\myshare1\e$\file.zip, Impersonation Enabled: 0, Token Valid: 1, HRESULT: 0x8007052e

Предлагаемая альтернатива - использовать WriteFile (), однако это проблематично, поскольку он загружает файл в память. В моем приложении файлы имеют размер> 200 МБ, поэтому масштабирование не будет.

Есть ли в ASP.NET метод для потоковой передачи файлов пользователям:

  • масштабируемый (не считывает весь файл в ОЗУ и не занимает потоки ASP.NET)
  • работает с UNC-акциями

Назначение сетевого диска в качестве виртуального каталога нам не подходит. Я также хотел бы избежать копирования файла на локальный веб-сервер.

Спасибо

Ответы [ 7 ]

1 голос
/ 17 ноября 2009

Вы пытались настроить vroot IIS, используя удаленный UNC-путь в качестве домашнего каталога? Если это работает, это может быть самым простым решением. Вы по-прежнему можете применять барьеры аутентификации для файлов (например, через HttpModule или, возможно, даже через готовый модуль для проверки подлинности форм), но вы можете положиться на IIS для эффективной потоковой передачи контента, как только ваши фильтры авторизации дадут добро.

Предупреждение: последний раз, когда я конфигурировал IIS в сценарии UNC, был очень давно (1998 !!!), и у меня возникли периодические проблемы с файлами, заблокированными на удаленном компьютере, что иногда приводило к проблемам с обновлением файлов. Работа с восстановлением после перезагрузки UNC-сервера также была интересной. Я предполагаю, что за 11 лет с тех пор эти проблемы были устранены, но вы никогда не можете быть уверены!

Другой подход может состоять в том, чтобы установить веб-сервер на компьютере UNC, а затем установить обратный прокси-сервер на своем веб-сервере, например, новый модуль Маршрутизация запросов приложений IIS7 .

Если вы хотите связать серверный поток, вы можете использовать подход, рекомендованный в KB812406 , который решает проблемы, связанные с использованием ОЗУ, тайм-аутами, отключением клиента и т. Д. Обязательно отключите Response Буферизация!

Идеальным решением для максимального контроля был бы «потоковый HttpHandler», в котором вместо необходимости отправлять выходные данные сразу, вы можете вернуть поток в ASP.NET и позволить ASP.NET разобраться с деталями о результатах разбиения на блоки. клиенту, занимающемуся отключениями и т. д. Но я не смог найти хороший способ сделать это. : - (

0 голосов
/ 24 ноября 2009

Код для TransmitFile очень прост, почему бы не изменить его, чтобы сделать то, что вам нужно?

public void TransmitFile(string filename, long offset, long length)
{
    if (filename == null)
    {
        throw new ArgumentNullException("filename");
    }
    if (offset < 0L)
    {
        throw new ArgumentException(SR.GetString("Invalid_range"), "offset");
    }
    if (length < -1L)
    {
        throw new ArgumentException(SR.GetString("Invalid_range"), "length");
    }
    filename = this.GetNormalizedFilename(filename);
    using (FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        long num = stream.Length;
        if (length == -1L)
        {
            length = num - offset;
        }
        if (num < offset)
        {
            throw new ArgumentException(SR.GetString("Invalid_range"), "offset");
        }
        if ((num - offset) < length)
        {
            throw new ArgumentException(SR.GetString("Invalid_range"), "length");
        }
        if (!this.UsingHttpWriter)
        {
            this.WriteStreamAsText(stream, offset, length);
            return;
        }
    }
    if (length > 0L)
    {
        bool supportsLongTransmitFile = (this._wr != null) && this._wr.SupportsLongTransmitFile;
        this._httpWriter.TransmitFile(filename, offset, length, this._context.IsClientImpersonationConfigured || HttpRuntime.IsOnUNCShareInternal, supportsLongTransmitFile);
    }
}



private void WriteStreamAsText(Stream f, long offset, long size)
{
    if (size < 0L)
    {
        size = f.Length - offset;
    }
    if (size > 0L)
    {
        if (offset > 0L)
        {
            f.Seek(offset, SeekOrigin.Begin);
        }
        byte[] buffer = new byte[(int) size];
        int count = f.Read(buffer, 0, (int) size);
        this._writer.Write(Encoding.Default.GetChars(buffer, 0, count));
    }
}


internal void TransmitFile(string filename, long offset, long size, bool isImpersonating, bool supportsLongTransmitFile)
{
    if (this._charBufferLength != this._charBufferFree)
    {
        this.FlushCharBuffer(true);
    }
    this._lastBuffer = null;
    this._buffers.Add(new HttpFileResponseElement(filename, offset, size, isImpersonating, supportsLongTransmitFile));
    if (!this._responseBufferingOn)
    {
        this._response.Flush();
    }
}

Спасибо,

Phil.

0 голосов
/ 24 ноября 2009

Я бы рекомендовал второй подход к сайту и реализовать механизм аутентификации на основе токенов. Кодировать куки-файл аутентификации в URL, передаваемом клиенту через Redirect. Это может быть непрозрачное значение, которое является общим за кулисами, или это может быть что-то простое, например, хэш секретного пароля и текущая дата, например.

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

0 голосов
/ 21 ноября 2009

Реальной ошибкой, которую вы получили, был сбой входа в общий файловый ресурс (что неудивительно, учитывая, что пользователь IIS, скорее всего, будет работать так же, как вы пытаетесь получить доступ к административному общему ресурсу). Вы пытались настроить общий ресурс, который вообще не имеет ограничений доступа, просто чтобы проверить, является ли это проблемой, а не каким-либо конкретным ограничением в TransmitFile.

Если это исправит это, то вам нужно войти в этот общий ресурс тем или иным образом как текущий пользователь или выдать себя за пользователя, у которого есть разрешения.

Стоит также указать, что после небольшого количества осмотров с отражателем TransmitFile может в любом случае потенциально закончить чтение файла в память, и что у WriteFile есть другая версия, которая принимает логическое значение, которое решает, читать файл в память или нет (на самом деле WriteFile по умолчанию передает false для этого параметра). Может быть, стоит побродить по коду.

0 голосов
/ 17 ноября 2009

Не могли бы вы настроить другой веб-сайт IIS, который указывает на UNC, а затем перенаправить их в файл на этом другом веб-сайте?

Response.Redirect ("http://files.somewhere.com/some/file.blah");

Таким образом, он будет выполняться в отдельном рабочем процессе и не будет влиять на ваш текущий сайт, а файлы будут обслуживаться непосредственно IIS, что явно лучше.

0 голосов
/ 17 ноября 2009

Вы можете записать файл в локальный каталог и получить задание robocopy, отслеживающее каталог, чтобы сделать копию.

Однако, поскольку вы хотите избежать записи на локальный сервер, вам может потребоваться установить сервер (HTTP или FTP, например) на целевом сервере и записать файл в эту службу.

0 голосов
/ 31 октября 2009

Можно попробовать открыть сетевой файл с помощью FileStream и использовать цикл для чтения фрагмента файла, передачи фрагмента (используя Response.Write(Char[], Int32, Int32)), удаления фрагмента и повторения до тех пор, пока файл не будет был полностью прочитан.

...