【问题标题】:Response.TransmitFile() with UNC share (ASP.NET)带有 UNC 共享的 Response.TransmitFile() (ASP.NET)
【发布时间】:2010-12-11 18:54:07
【问题描述】:

在本页的cmets中:

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(),但是,这是有问题的,因为它将文件加载到内存中。在我的应用程序中,文件大于 200MB,因此无法扩展。

在 ASP.NET 中是否有一种方法可以将文件流式传输给用户:

  • 可扩展(不会将整个文件读入 RAM 或占用 ASP.NET 线程)
  • 适用于 UNC 共享

将网络驱动器映射为虚拟目录不是我们的选择。我也想避免将文件复制到本地 Web 服务器。

谢谢

【问题讨论】:

    标签: asp.net download impersonation content-disposition transmitfile


    【解决方案1】:

    您是否尝试过使用远程 UNC 路径作为其主目录来设置 IIS vroot?如果可行,这可能是最简单的解决方案。您仍然可以对文件强制执行身份验证障碍(例如,通过 HttpModule,或者甚至可能通过开箱即用的表单身份验证模块),但是一旦您的身份验证过滤器允许,您可以依靠 IIS 有效地流式传输内容。

    警告:我上一次在 UNC 场景中配置 IIS 是很久以前(1998 年!!!),我遇到了文件被锁定在远程计算机上的间歇性问题,导致更新文件有时会出现问题。处理 UNC 服务器重新启动后的恢复也很有趣。我想从那以后的 11 年里,这些问题已经解决了,但你永远无法确定!

    另一种方法可能是在 UNC 机器上安装 Web 服务器,然后在您的 Web 服务器上安装反向代理服务器,例如新的 IIS7 Application Request Routing 模块。

    如果您愿意占用一个服务器线程,您可以使用KB812406 中推荐的方法,该方法处理有关 RAM 消耗、超时、客户端断开连接等问题。请务必关闭响应缓冲!

    理想的最大控制解决方案是“流式 HttpHandler”,您可以将流返回到 ASP.NET 并让 ASP.NET 处理有关分块结果的详细信息,而不是一次发送所有输出到客户端,处理断开连接等。但我无法找到一个好的方法来做到这一点。 :-(

    【讨论】:

    • IIS7 应用程序请求路由模块听起来可能对我有用......会试一试。
    【解决方案2】:

    您可以尝试使用FileStream 打开网络文件,并使用循环读取文件的一个块,传输该块(使用Response.Write(Char[], Int32, Int32)),处理该块,然后重复直到文件已完全阅读。

    【讨论】:

    • 其中一个问题是它在整个文件传输过程中占用了一个 ASP.NET 工作进程线程。所以它可能不像 TransmitFile() 那样可扩展。
    • 是的。但这可能是您必须接受的快乐媒介,因为您正在寻找的东西似乎没有任何内置功能。
    • 这种方法还有脚本超时的风险,不是吗?
    【解决方案3】:

    您可以将文件写入本地目录,并让 robocopy 作业监视目录以进行复制。

    不过,由于您想避免写入本地服务器,您可能需要调查将服务器(例如 HTTP 或 FTP)放在目标服务器上,并将文件写入该服务。

    【讨论】:

    • 这也是我的解决方案,但它不适合任何可能负载平衡的东西。我们有一个客户实际使用这种方法,并且每隔 30 秒在站点中的所有文件夹中运行一次 robo,当一个 robo 踩到另一个时它会失败几次。
    • 我在负载平衡的环境中运行。正如你提到的,我想避免写入本地网络服务器。
    【解决方案4】:

    您能否设置另一个指向 UNC 的 IIS 网站,然后将它们重定向到其他网站上的文件?

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

    这样它将在单独的工作进程中运行,对您当前的站点没有影响,并且文件将直接由 IIS 提供,这显然是最好的。

    【讨论】:

    • 这个问题是如果有人知道 URL,这些文件将可用于开放访问。从同一进程提供文件意味着我可以使用表单身份验证来限制对文件的访问。
    • 好吧,我不认为你会找到一个好的解决方案,你想在没有任何 asp.net 参与的情况下提供文件,使用 asp.net autentication...你需要找到一个之间的解决方案。
    【解决方案5】:

    您返回的实际错误是文件共享的登录失败(考虑到用户 IIS 可能正在运行,并且您尝试访问管理共享,这并不奇怪)。您是否尝试过设置一个完全没有访问限制的共享,只是为了检查这是否是问题所在,而不是 TransmitFile 中的任何特定限制。

    如果解决了问题,那么您需要以一种或另一种方式以当前用户身份登录到该共享,或者冒充具有权限的用户。

    还值得指出的是,在使用反射器 TransmitFile 环顾四周之后,无论如何都可能最终将文件读入内存,并且 WriteFile 有另一个版本,它采用布尔值来决定是否将文件读入内存或不是(实际上默认的 WriteFile 为这个参数传递了 false )。可能值得您在代码中四处寻找。

    【讨论】:

    • 我已经删除了对共享的访问限制 - 没有帮助。
    【解决方案6】:

    我会推荐第二种站点方法并实施基于令牌的身份验证机制。在通过重定向传递给客户端的 URL 中编码身份验证 cookie。这可能是在幕后共享的不透明值,也可能是密码和当前日期的哈希值一样简单。

    我做过的一个项目使用了散列。一台服务器生成了一个共享密码和一个分机号码的哈希值。第二台服务器(在不同的网络上)采用相同的密码和分机,并对它们进行哈希处理,以确认用户可以从该分机拨打所需的电话号码。

    【讨论】:

    • 第二个站点是ASP.NET吗?它如何处理 cookie?
    • 第一个站点是 PHP,第二个站点是使用 HttpListener 的自定义 C# 服务。然而,这种技术适用于任何地方。
    【解决方案7】:

    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();
        }
    }
    

    谢谢,

    菲尔。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-28
      • 2012-05-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-30
      相关资源
      最近更新 更多