1

我想向网站用户提供可下载的文件,但想对用户隐藏文件的 URL ...我认为 HTTPHandler 可以解决问题,但是否可以从外部服务器检索文件和将其流式传输给用户?

也许有人可以给我一个提示,告诉我如何做到这一点,或者给我指出一个以前做过的资源?


只是为了详细说明我想要实现的目标......我正在构建一个 ASP.NET 网站,其中包含一个音乐下载链接。我想保护文件的实际 URL,我还想将它们存储在外部(PHP)服务器上(便宜得多)......

所以我需要做的是设置一个流,它可以从 URL 中获取文件(指向另一台服务器),并将其流式传输到 Response 对象,而用户不会意识到它来自另一台服务器。

TransmitFile 方法是否允许从完全独立的服务器流式传输文件?我不希望文件“通过”我的服务器流式传输,因为这违背了目的(节省带宽)......我希望客户端(浏览器)直接从另一台服务器下载文件。

我可能需要文件托管服务器上的处理程序吗?也许另一端的PHP脚本是要走的路......?

4

5 回答 5

1

我建议您查看 TransmitFile 方法:http: //msdn.microsoft.com/en-us/library/12s31dhy.aspx

于 2008-10-02T02:40:57.780 回答
1

随着您澄清希望带宽来自外部服务器而不是您的服务器,它在很大程度上改变了这个问题。

为了实现这一点,外部服务器必须有一个网站,您可以将用户发送到该网站。您不能通过您的站点流式传输文件但不会受到带宽的影响,或者无法从您的站点控制它但通过其他服务器流式传输,因此必须通过其他站点完全处理它。问题在于,基于 URL 的普通方法会向用户显示 URL,您所说的第二个要求是它不显示 URL。

但是,您难道不能只有一个通用页面来提供外部站点上的文件,而流式传输的文件的细节将通过原始站点页面上的帖子传递吗?这将删除指向特定文件的 URL。它会显示域,但用户将无法在不知道帖子字段的情况下提取文件。

这不需要是一个 HTTPHandler,只是一个普通的页面。

于 2008-10-02T14:08:13.057 回答
1

是的,您可以从远程流(从另一台服务器下载)流式传输到输出流。假设serviceUrl是要流式传输的文件的位置:

HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(serviceUrl);
            webrequest.AllowAutoRedirect = false;
            webrequest.Timeout = 30 * 1000;
            webrequest.ReadWriteTimeout = 30 * 1000;
            webrequest.KeepAlive = false;

            Stream remoteStream = null;
            byte[] buffer = new byte[4 * 1024];
            int bytesRead;

            try {
                WebResponse responce = webrequest.GetResponse();
                remoteStream = responce.GetResponseStream();
                bytesRead = remoteStream.Read(buffer, 0, buffer.Length);

                Server.ScriptTimeout = 30 * 60;
                Response.Buffer = false;
                Response.BufferOutput = false;
                Response.Clear();
                Response.ContentType = "application/octet-stream";
                Response.AppendHeader("Content-Disposition", "attachment; filename=" + Uid + ".EML");
                if (responce.ContentLength != -1)
                    Response.AppendHeader("Content-Length", responce.ContentLength.ToString());

                while (bytesRead > 0 && Response.IsClientConnected) {
                    Response.OutputStream.Write(buffer, 0, bytesRead);
                    bytesRead = remoteStream.Read(buffer, 0, buffer.Length);
                }

            } catch (Exception E) {
                Logger.LogErrorFormat(LogModules.DomainUsers, "Error transfering message from remote host: {0}", E.Message);
                Response.End();
                return;
            } finally {
                if (remoteStream != null) remoteStream.Close();
            }

            Response.End();
于 2008-12-06T02:11:15.243 回答
0

我以前做过。首先,很明显,文件必须在网站用户进程可以访问的外部服务器上的共享中。

就 HTTPHandler 而言,我通过为用户提供包含他们想要下载的文件的 zip 文件来处理这个问题;这样,我的处理程序可以拦截对 .zip 文件的任何调用,并将它们流式传输到我创建的 zip 文件中。

这是代码(相当大块;我使用 MVP,所以它分为 Handler 和 Presenter): Handler:

public class ZipDownloadModule: IHttpHandler, ICompressFilesView, IErrorView 
{
    CompressFilesPresenter _presenter;

    public ZipDownloadModule()
    {
        _presenter = new CompressFilesPresenter(this, this);
    }
    #region IHttpHandler Members

    public bool IsReusable
    {
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        OnDownloadFiles();
    }

    private void OnDownloadFiles()
    {
        if(Compress != null)
            Compress(this, EventArgs.Empty);
    }

    #endregion

    #region IFileListDownloadView Members

    public IEnumerable<string> FileNames
    {
        get 
        {
            string files = HttpContext.Current.Request["files"] ?? string.Empty;

            return files.Split(new Char[] { ',' });
        }
    }

    public System.IO.Stream Stream
    {
        get
        {
            HttpContext.Current.Response.ContentType = "application/x-zip-compressed";
            HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename=ads.zip");
            return HttpContext.Current.Response.OutputStream;
        }
    }

    public event EventHandler Compress;

    #endregion

    #region IErrorView Members

    public string errorMessage
    {
        set {  }
    }

    #endregion
}

主持人:

public class CompressFilesPresenter: PresenterBase<ICompressFilesView>
{
    IErrorView _errorView;

    public CompressFilesPresenter(ICompressFilesView view, IErrorView errorView)
        : base(view)
    {
        _errorView = errorView;
        this.View.Compress += new EventHandler(View_Compress);
    }

    void View_Compress(object sender, EventArgs e)
    {
        CreateZipFile();
    }

    private void CreateZipFile()
    {
        MemoryStream stream = new MemoryStream();

        try
        {
            CreateZip(stream, this.View.FileNames);

            WriteZip(stream);
        }
        catch(Exception ex)
        {
            HandleException(ex);
        }
    }

    private void WriteZip(MemoryStream stream)
    {
        byte[] data = stream.ToArray();

        this.View.Stream.Write(data, 0, data.Length);
    }

    private void CreateZip(MemoryStream stream, IEnumerable<string> filePaths)
    {
        using(ZipOutputStream s = new ZipOutputStream(stream)) // this.View.Stream))
        {
            s.SetLevel(9); // 0 = store only to 9 = best compression

            foreach(string fullPath in filePaths)
                AddFileToZip(fullPath, s);

            s.Finish();
        }
    }

    private static void AddFileToZip(string fullPath, ZipOutputStream s)
    {
        byte[] buffer = new byte[4096];

        ZipEntry entry;

        // Using GetFileName makes the result compatible with XP
        entry = new ZipEntry(Path.GetFileName(fullPath));

        entry.DateTime = DateTime.Now;
        s.PutNextEntry(entry);

        using(FileStream fs = File.OpenRead(fullPath))
        {
            int sourceBytes;
            do
            {
                sourceBytes = fs.Read(buffer, 0, buffer.Length);
                s.Write(buffer, 0, sourceBytes);
            } while(sourceBytes > 0);
        }
    }

    private void HandleException(Exception ex)
    {
        switch(ex.GetType().ToString())
        {
            case "DirectoryNotFoundException":
                _errorView.errorMessage = "The expected directory does not exist.";
                break;
            case "FileNotFoundException":
                _errorView.errorMessage = "The expected file does not exist.";
                break;
            default:
                _errorView.errorMessage = "There has been an error. If this continues please contact AMG IT Support.";
                break;
        }
    }

    private void ClearError()
    {
        _errorView.errorMessage = "";
    }
}

希望这可以帮助!!

于 2008-10-02T02:55:49.960 回答
0

好吧,似乎我避免编写/部署一些 php 代码的努力是徒劳的......这是我将在文件(php)服务器上运行的内容:

http://www.zubrag.com/scripts/download.php

然后来自我的 asp.net 网络服务器的链接将指向该脚本,然后该脚本将下载相关文件(因此避免直接下载,并允许通过谷歌分析跟踪下载)......我认为这样就可以了

谢谢大家格雷格

于 2008-10-02T23:38:13.437 回答