2015-11-16 2 views
1

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

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

Клиент будет посылать данные XML на сервер, который будет выглядеть примерно так:

<Data xmlns="http://schemas.datacontract.org/2004/07/DataFiles" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 
    <Category>General</Category> 
    <Files> 
     <DataFile> 
      <Extension>.txt</Extension> 
      <Filename>test4</Filename> 
      <Bytes>"Some binary data"</Bytes> 
     </DataFile> 
    </Files> 
</Data> 

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

I then переместился на TcpListener, чтобы перейти на более низкий уровень, который, кажется, отлично работает на больших файлах, поскольку они отправляются фрагментированными, но оставляю мне делать много работы, чтобы добавить пакет на стороне сервера при получении запроса.

Я также переехал за WCF как возможность, но мой недостаток опыта в этой технологии заставил меня снова оставить этот подход.

Что вы должны сделать? Какой инструмент .NET большой инструментарий .NET вы бы использовали для создания FTP-сервера/клиента?

Существует много тем о TcpListeners и т. Д., Это не то, что я ищу здесь. Мне нужны советы, по какому подходу я должен идти, и лучшие практики.

EDIT: Забыл упомянуть, что идея позади него больше похож на FTP-прокси (клиент отправить файл на сервер> магазин сервера файл локально> Сервер отправляет его на 3 части места> Сервер ясно локально сохраненный файл когда файл seding на место третьей части выполняется успешно).

EDIT 17-11-15:

Вот пример кода, как я делаю HTTP-сервер:

public class HttpServer 
{ 
    protected readonly HttpListener HttpListener = new HttpListener(); 

    protected HttpServer(IEnumerable<string> prefixes) 
    { 
     HttpListener.Prefixes.Add(prefix); 
    } 

    public void Start() 
    { 
     while (HttpListener.IsListening && Running) 
     { 
      var result = HttpListener.BeginGetContext(ContextReceived, HttpListener); 
      if (WaitHandle.WaitAny(new[] {result.AsyncWaitHandle, _shutdown}) == 0) 
       return; 
     } 
    } 

    protected object ReadRequest(HttpListenerRequest request) 
    { 
     using (var input = request.InputStream) 
     using (var reader = new StreamReader(input, request.ContentEncoding)) 
     { 
      var data = reader.ReadToEnd(); 
      return data; 
     } 
    } 

    protected void ContextReceived(IAsyncResult ar) 
    { 
     HttpListenerContext context = null; 
     HttpListenerResponse response = null; 
     try 
     { 
      var listener = ar.AsyncState as HttpListener; 
      if (listener == null) throw new InvalidCastException("ar"); 
      context = listener.EndGetContext(ar); 
      response = context.Response; 
      switch (context.Request.HttpMethod) 
      { 
       case WebRequestMethods.Http.Post: 
        // Parsing XML data with file at LARGE byte[] as one of the parameter, seems to struggle here... 
        break; 
       default: 
        //Send MethodNotAllowed response.. 
        break; 
      } 
      response.Close(); 
     } 
     catch(Exception ex) 
     { 
      //Do some properly exception handling!! 
     } 
     finally 
     { 
      if (context != null) 
      { 
       context.Response.Close(); 
      } 
      if (response != null) 
       response.Close(); 
     } 
    } 
} 

Клиент использует:

using (var client = new WebClient()) 
{ 
    GetExtensionHeaders(client.Headers); 
    client.Encoding = Encoding.UTF8; 
    client.UploadFileAsync(host, fileDialog.FileName ?? "Test"); 
    client.UploadFileCompleted += ClientOnUploadFileCompleted; 
    client.UploadProgressChanged += ClientOnUploadProgressChanged; 
} 

Пожалуйста, обратите внимание, что клиент должен отправить данные (как XML) на сервер, которые будут десериализовать полученные данные (используя серверный сервер), как было написано ранее.

Вот мой TCPServer пример:

public class TcpServer 
{ 
    protected TcpListener Listener; 
    private bool _running; 

    public TcpServer(int port) 
    { 
     Listener = new TcpListener(IPAddress.Any, port); 
     Console.WriteLine("Listener started @ {0}:{1}", ((IPEndPoint)Listener.LocalEndpoint).Address, ((IPEndPoint)Listener.LocalEndpoint).Port); 
     _running = true; 
    } 

    protected readonly ManualResetEvent TcpClientConnected = new ManualResetEvent(false); 
    public void Start() 
    { 
     while (_running) 
     { 
      TcpClientConnected.Reset(); 
      Listener.BeginAcceptTcpClient(AcceptTcpClientCallback, Listener); 
      TcpClientConnected.WaitOne(TimeSpan.FromSeconds(5)); 
     } 
    } 

    protected void AcceptTcpClientCallback(IAsyncResult ar) 
    { 
     try 
     { 
      var listener = ar.AsyncState as TcpListener; 
      if (listener == null) return; 

      using (var client = listener.EndAcceptTcpClient(ar)) 
      { 
       using (var stream = client.GetStream()) 
       { 
        //Append or create to file stream 
       } 
      } 

      //Parse XML data received? 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e); 
     } 
     finally 
     { 
      TcpClientConnected.Set(); 
     } 
    } 
} 
+4

Я призываю вас к следующему «поскольку контекст получен как один пакет, не фрагментированный». Можете ли вы объяснить, что это значит? – spender

+0

Ну, я заметил, что когда клиент отправляет файл на сервер (используя TcpClient в качестве клиента), большое количество пакетов tcp отправляется на хост/сервер в соответствии с трассировкой wirehark. Но контекст не существует waithandle, пока не будет отправлен полный пакет HTTP/XML. – grmihel

+2

Вы в этом уверены? Я прочитал очень большие HTTP-потоки от клиента, используя HttpListener, не дожидаясь, пока вся полезная нагрузка достигнет сервера. Посмотрим на код. Я думаю, вы делаете это неправильно. – spender

ответ

2

Создание нового пустого приложения MVC

enter image description here

Следующая добавить новый контроллер в папку Controllers,

using System.Web; 
using System.Web.Mvc; 

namespace UploadExample.Controllers 
{ 
    public class UploadController : Controller 
    { 
     public ActionResult File(HttpPostedFileBase file) 
     { 
      file.SaveAs(@"c:\FilePath\" + file.FileName); 
     } 

    } 
} 

сейчас все, что вам нужно сделать, чтобы загрузить документ i s разместите его на своем сайте в виде данных с несколькими форматами ...

void Main() 
{ 
    string fileName = @"C:\Test\image.jpg"; 
    string uri = @"http://localhost/Upload/File"; 
    string contentType = "image/jpeg"; 

    Http.Upload(uri, fileName, contentType); 
} 

public static class Http 
{ 
    public static void Upload(string uri, string filePath, string contentType) 
    { 
     string boundary   = "---------------------------" + DateTime.Now.Ticks.ToString("x"); 
     byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n"); 

     string formdataTemplate = "Content-Disposition: form-data; name=file; filename=\"{0}\";\r\nContent-Type: {1}\r\n\r\n"; 
     string formitem   = string.Format(formdataTemplate, Path.GetFileName(filePath), contentType); 
     byte[] formBytes  = Encoding.UTF8.GetBytes(formitem); 

     HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri); 
     request.KeepAlive  = true; 
     request.Method   = "POST"; 
     request.ContentType  = "multipart/form-data; boundary=" + boundary; 
     request.SendChunked  = true; 

     using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) 
     using (Stream requestStream = request.GetRequestStream()) 
     { 
      requestStream.Write(boundaryBytes, 0, boundaryBytes.Length); 
      requestStream.Write(formBytes, 0, formBytes.Length); 

      byte[] buffer = new byte[1024*4]; 
      int bytesLeft; 

      while ((bytesLeft = fileStream.Read(buffer, 0, buffer.Length)) > 0) requestStream.Write(buffer, 0, bytesLeft); 

      requestStream.Write(boundaryBytes, 0, boundaryBytes.Length); 
     } 

     using (HttpWebResponse response = (HttpWebResponse) request.GetResponse()) 
     { 
     } 

     Console.WriteLine ("Success");  
    } 
} 

EDIT

Если вы столкнулись с проблемами, редактировать файл web.config, вполне вероятно, что вы удара ограничения длины запроса ...

<system.web> 
    <compilation debug="true" targetFramework="4.5"/> 
    <httpRuntime targetFramework="4.5" maxRequestLength="1048576"/> 
</system.web> 

Еще одна вещь, которую я пропустил (но теперь отредактированный) был переданный chunked свойство на webrequest его сам.

request.SendChunked = true; 
+0

Спасибо за подробный пример. Возможно, я пропустил его, но серверная сторона должна работать как самообслуживаемая служба Windows и не зависит от запуска службы IIS.Может ли ваш пример работать без службы IIS (извините за отсутствие опыта asp.net) – grmihel