带有UNC共享的Response.TransmitFile()(ASP.NET)

时间:2009-10-30 23:33:37

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

在本页的评论中:

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服务器。

由于

7 个答案:

答案 0 :(得分: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处理有关分块结果的详细信息,而不必一次性发送输出。对客户,处理断开连接等。但我无法找到一个好方法来做到这一点。 : - (

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

你可以设置另一个指向UNC的IIS网站,然后将它们重定向到另一个网站上的文件吗?

的Response.Redirect( “http://files.somewhere.com/some/file.blah”);

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

答案 4 :(得分:0)

您获得的实际错误是文件共享登录失败(考虑到用户IIS可能正在运行,以及您尝试访问管理共享时,这并不是非常令人惊讶)。您是否尝试过设置一个完全没有访问限制的共享来检查这是否是问题而不是TransmitFile中的任何特定限制。

如果修复它,那么您需要以当前用户的方式登录该共享,或模拟具有权限的用户。

值得指出的是,在使用反射器环顾四周后,无论如何,最终可能会将文件读入内存,而WriteFile还有另一个版本,该版本采用布尔值来决定是将文件读入内存还是not(事实上,默认的WriteFile为此参数传递false)。可能值得你在代码中探讨。

答案 5 :(得分:0)

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

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

答案 6 :(得分:-1)

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

谢谢,

菲尔。