HTTPHandler, чтобы получить файл загрузки с другого сервера? - PullRequest
1 голос
/ 02 октября 2008

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

Возможно, кто-нибудь может дать мне подсказку о том, как это сделать, или указать мне ресурс, где это было сделано раньше?


Просто чтобы уточнить, чего я пытаюсь достичь ... Я создаю веб-сайт ASP.NET, который содержит ссылку на скачивание музыки. Я хочу защитить действительные URL-адреса файла, а также сохранить их на внешнем (PHP) сервере (НАМНОГО более дешевым) ...

Итак, мне нужно настроить поток, который может захватывать файл с URL-адреса (указывает на другой сервер) и передавать его на объект Response, при этом пользователь не осознает, что он поступает с другого сервера.

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

Нужен ли мне обработчик на сервере хостинга файлов? Может быть, PHP-скрипт на другом конце - это путь ...?

Ответы [ 5 ]

1 голос
/ 06 декабря 2008

Да, вы можете осуществлять потоковую передачу из удаленного потока (загрузка с другого сервера) в выходной поток. Предположим, 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();
1 голос
/ 02 октября 2008

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

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

Но разве вы не могли просто иметь общую страницу, которая обслуживает файлы на внешнем сайте, и сведения о том, какой файл для потоковой передачи будет передаваться через сообщение со страницы на исходном сайте? Это удалит URL, указывающий на определенный файл. Будет показан домен, но пользователи не смогут извлекать файлы, не зная полей записи.

Это не должен быть HTTPHandler, просто обычная страница.

1 голос
/ 02 октября 2008

Рекомендую посмотреть на метод TransmitFile: http://msdn.microsoft.com/en-us/library/12s31dhy.aspx

0 голосов
/ 03 октября 2008

Ладно, похоже, что мое стремление избежать написания / развертывания некоторого php-кода напрасно ... вот что я собираюсь запустить на файловом (php) сервере:

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

Тогда ссылки с моего веб-сервера asp.net будут указывать на этот скрипт, который затем будет загружать соответствующий файл (следовательно, избегать прямых загрузок и разрешать отслеживание загрузок с помощью Google Analytics) ... Я думаю, что это подойдет трюк

Спасибо всем Грег

0 голосов
/ 02 октября 2008

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

Что касается HTTPHandler, я справился с этим, предоставив пользователям zip-файлы, содержащие файлы, которые они хотят загрузить; таким образом, мой обработчик может перехватить любой вызов для файлов .zip и передать их потоку zip-файла, который я создаю.

Вот код (довольно много; я использую MVP, поэтому он разделен на Handler и Presenter): Обработчик:

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 = "";
    }
}

Надеюсь, это поможет !!

...