2011-12-11 13 views
11

Sto cercando di recuperare un file caricato dal mio server web. Mentre il client invia i suoi file tramite un modulo web (file casuali), devo analizzare la richiesta per ottenere il file e elaborarlo ulteriormente. In sostanza, il codice va come:Hplistener e caricamento file

HttpListenerContext context = listener.GetContext(); 
HttpListenerRequest request = context.Request; 
StreamReader r = new StreamReader(request.InputStream, System.Text.Encoding.Default); 
// this is the retrieved file from streamreader 
string file = null; 

while ((line = r.ReadLine()) != null){ 
    // i read the stream till i retrieve the filename 
    // get the file data out and break the loop 
} 
// A byststream is created by converting the string, 
Byte[] bytes = request.ContentEncoding.GetBytes(file); 
MemoryStream mstream = new MemoryStream(bytes); 

// do the rest 

Di conseguenza, sono in grado di recuperare i file di testo, ma anche per tutti gli altri file, che sono danneggiati. Qualcuno potrebbe dirmi come analizzare correttamente questi HttplistnerRequests (o fornire un'alternativa più leggera)?

+0

lo prendo stai usando enctype = "/ form-data multipart" nel modulo web? Se così sembra che si sta banalizzando la strada si legge il contenuto. –

risposta

13

Penso che si stanno facendo le cose più difficili su se stessi del necessario in questo modo, con un HttpListener anziché utilizzare le funzionalità integrate di ASP.Net. Ma se devi farlo in questo modo ecco alcuni esempi di codice. Nota: 1) Suppongo che tu stia utilizzando enctype="multipart/form-data" sul tuo <form>. 2) Questo codice è progettato per essere utilizzato con un modulo contenente solo il tuo <input type="file" /> se vuoi pubblicare altri campi o più file dovrai cambiare il codice. 3) Questo è pensato per essere un proof of concept/esempio, può avere bug e non è particolarmente flessibile.

static void Main(string[] args) 
{ 
    HttpListener listener = new HttpListener(); 
    listener.Prefixes.Add("http://localhost:8080/ListenerTest/"); 
    listener.Start(); 

    HttpListenerContext context = listener.GetContext(); 

    SaveFile(context.Request.ContentEncoding, GetBoundary(context.Request.ContentType), context.Request.InputStream); 

    context.Response.StatusCode = 200; 
    context.Response.ContentType = "text/html"; 
    using (StreamWriter writer = new StreamWriter(context.Response.OutputStream, Encoding.UTF8)) 
     writer.WriteLine("File Uploaded"); 

    context.Response.Close(); 

    listener.Stop(); 

} 

private static String GetBoundary(String ctype) 
{ 
    return "--" + ctype.Split(';')[1].Split('=')[1]; 
} 

private static void SaveFile(Encoding enc, String boundary, Stream input) 
{ 
    Byte[] boundaryBytes = enc.GetBytes(boundary); 
    Int32 boundaryLen = boundaryBytes.Length; 

    using (FileStream output = new FileStream("data", FileMode.Create, FileAccess.Write)) 
    { 
     Byte[] buffer = new Byte[1024]; 
     Int32 len = input.Read(buffer, 0, 1024); 
     Int32 startPos = -1; 

     // Find start boundary 
     while (true) 
     { 
      if (len == 0) 
      { 
       throw new Exception("Start Boundaray Not Found"); 
      } 

      startPos = IndexOf(buffer, len, boundaryBytes); 
      if (startPos >= 0) 
      { 
       break; 
      } 
      else 
      { 
       Array.Copy(buffer, len - boundaryLen, buffer, 0, boundaryLen); 
       len = input.Read(buffer, boundaryLen, 1024 - boundaryLen); 
      } 
     } 

     // Skip four lines (Boundary, Content-Disposition, Content-Type, and a blank) 
     for (Int32 i = 0; i < 4; i++) 
     { 
      while (true) 
      { 
       if (len == 0) 
       { 
        throw new Exception("Preamble not Found."); 
       } 

       startPos = Array.IndexOf(buffer, enc.GetBytes("\n")[0], startPos); 
       if (startPos >= 0) 
       { 
        startPos++; 
        break; 
       } 
       else 
       { 
        len = input.Read(buffer, 0, 1024); 
       } 
      } 
     } 

     Array.Copy(buffer, startPos, buffer, 0, len - startPos); 
     len = len - startPos; 

     while (true) 
     { 
      Int32 endPos = IndexOf(buffer, len, boundaryBytes); 
      if (endPos >= 0) 
      { 
       if (endPos > 0) output.Write(buffer, 0, endPos-2); 
       break; 
      } 
      else if (len <= boundaryLen) 
      { 
       throw new Exception("End Boundaray Not Found"); 
      } 
      else 
      { 
       output.Write(buffer, 0, len - boundaryLen); 
       Array.Copy(buffer, len - boundaryLen, buffer, 0, boundaryLen); 
       len = input.Read(buffer, boundaryLen, 1024 - boundaryLen) + boundaryLen; 
      } 
     } 
    } 
} 

private static Int32 IndexOf(Byte[] buffer, Int32 len, Byte[] boundaryBytes) 
{ 
    for (Int32 i = 0; i <= len - boundaryBytes.Length; i++) 
    { 
     Boolean match = true; 
     for (Int32 j = 0; j < boundaryBytes.Length && match; j++) 
     { 
      match = buffer[i + j] == boundaryBytes[j]; 
     } 

     if (match) 
     { 
      return i; 
     } 
    } 

    return -1; 
} 

per aiutarvi a capire meglio ciò che il codice di cui sopra sta facendo, qui è ciò che il corpo del HTTP POST assomiglia:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9lcB0OZVXSqZLbmv 

------WebKitFormBoundary9lcB0OZVXSqZLbmv 
Content-Disposition: form-data; name="my_file"; filename="Test.txt" 
Content-Type: text/plain 

Test 
------WebKitFormBoundary9lcB0OZVXSqZLbmv-- 

ho lasciato fuori le intestazioni irrilevanti. Come si può vedere, è necessario analizzare il corpo attraverso la scansione attraverso di trovare l'inizio e la fine delle sequenze di contorno, e rilasciare le intestazioni secondarie che vengono prima del contenuto del file. Sfortunatamente non è possibile utilizzare StreamReader a causa del potenziale per i dati binari. Anche sfortunato è il fatto che non c'è nessun file per Content-Length (l'intestazione Content-Length per la richiesta specifica la lunghezza totale del corpo compresi i limiti, i sottotitoli e la spaziatura.

+0

Ho provato questo su un file JPEG e un file di testo ed entrambi hanno funzionato. –

+0

Ciao, grazie per questa risposta elaborata! Funziona davvero e mi aiuta molto! (anche se questo non è il modo più efficiente) – cecemel

+0

Questo è quello che sto cercando. Grazie – fyasar

1

Il problema è che stai leggendo il file come testo.

è necessario leggere il file come un ByteArray invece e utilizzando il BinaryReader è migliore e più facile da usare rispetto StreamReader:

Byte[] bytes; 
using (System.IO.BinaryReader r = new System.IO.BinaryReader(request.InputStream)) 
{ 
    // Read the data from the stream into the byte array 
    bytes = r.ReadBytes(Convert.ToInt32(request.InputStream.Length)); 
} 
MemoryStream mstream = new MemoryStream(bytes); 
+0

Ciao, grazie per la risposta! sembra tuttavia che la request.InputStream.Length fornisce un'eccezione non supportato. Inoltre, ByteArray complica il lavoro in quanto v'è una certa analisi che hanno bisogno s per estrarre il file da altri contenuti del corpo. Streamrider sembrava da quel punto di vista più adatta allo scopo. – cecemel

+0

@ user1092608: Oh, questo è sfortunato. Il problema è che non si possono realmente usare le capacità di lettura del testo di streamreader per estrarre un file binario. Stai utilizzando un controllo FileUpload o qualche altro metodo per incorporare il file nello stream? –

+0

Questa è un'osservazione pertinente. In questo momento il client utilizza fondamentalmente l'html predefinito form. Se possibile, vorrei evitare il controllo di FileUpload, per mantenere le cose semplici come possibile -wathever questo ancora means. In questo progetto, un altro caso (macchina) dovrebbe essere in grado di lanciare file casuali al mio server, insieme ad alcuni metadati, per fare cose con esso e inviare i risultati indietro. – cecemel

1

Può avere bug, testare accuratamente . Questo si ottiene tutto pOST, GET e file.

using System.Collections.Generic; 
using System.Collections.Specialized; 
using System.IO; 
using System.Net; 
using System.Text; 
using System.Web; 

namespace DUSTLauncher 
{ 
    class HttpNameValueCollection 
    { 
     public class File 
     { 
      private string _fileName; 
      public string FileName { get { return _fileName ?? (_fileName = ""); } set { _fileName = value; } } 

      private string _fileData; 
      public string FileData { get { return _fileData ?? (_fileName = ""); } set { _fileData = value; } } 

      private string _contentType; 
      public string ContentType { get { return _contentType ?? (_contentType = ""); } set { _contentType = value; } } 
     } 

     private NameValueCollection _get; 
     private Dictionary<string, File> _files; 
     private readonly HttpListenerContext _ctx; 

     public NameValueCollection Get { get { return _get ?? (_get = new NameValueCollection()); } set { _get = value; } } 
     public NameValueCollection Post { get { return _ctx.Request.QueryString; } } 
     public Dictionary<string, File> Files { get { return _files ?? (_files = new Dictionary<string, File>()); } set { _files = value; } } 

     private void PopulatePostMultiPart(string post_string) 
     { 
      var boundary_index = _ctx.Request.ContentType.IndexOf("boundary=") + 9; 
      var boundary = _ctx.Request.ContentType.Substring(boundary_index, _ctx.Request.ContentType.Length - boundary_index); 

      var upper_bound = post_string.Length - 4; 

      if (post_string.Substring(2, boundary.Length) != boundary) 
       throw (new InvalidDataException()); 

      var raw_post_strings = new List<string>(); 
      var current_string = new StringBuilder(); 

      for (var x = 4 + boundary.Length; x < upper_bound; ++x) 
      { 
       if (post_string.Substring(x, boundary.Length) == boundary) 
       { 
        x += boundary.Length + 1; 
        raw_post_strings.Add(current_string.ToString().Remove(current_string.Length - 3, 3)); 
        current_string.Clear(); 
        continue; 
       } 

       current_string.Append(post_string[x]); 

       var post_variable_string = current_string.ToString(); 

       var end_of_header = post_variable_string.IndexOf("\r\n\r\n"); 

       if (end_of_header == -1) throw (new InvalidDataException()); 

       var filename_index = post_variable_string.IndexOf("filename=\"", 0, end_of_header); 
       var filename_starts = filename_index + 10; 
       var content_type_starts = post_variable_string.IndexOf("Content-Type: ", 0, end_of_header) + 14; 
       var name_starts = post_variable_string.IndexOf("name=\"") + 6; 
       var data_starts = end_of_header + 4; 

       if (filename_index == -1) continue; 

       var filename = post_variable_string.Substring(filename_starts, post_variable_string.IndexOf("\"", filename_starts) - filename_starts); 
       var content_type = post_variable_string.Substring(content_type_starts, post_variable_string.IndexOf("\r\n", content_type_starts) - content_type_starts); 
       var file_data = post_variable_string.Substring(data_starts, post_variable_string.Length - data_starts); 
       var name = post_variable_string.Substring(name_starts, post_variable_string.IndexOf("\"", name_starts) - name_starts); 
       Files.Add(name, new File() { FileName = filename, ContentType = content_type, FileData = file_data }); 
       continue; 

      } 
     } 

     private void PopulatePost() 
     { 
      if (_ctx.Request.HttpMethod != "POST" || _ctx.Request.ContentType == null) return; 

      var post_string = new StreamReader(_ctx.Request.InputStream, _ctx.Request.ContentEncoding).ReadToEnd(); 

      if (_ctx.Request.ContentType.StartsWith("multipart/form-data")) 
       PopulatePostMultiPart(post_string); 
      else 
       Get = HttpUtility.ParseQueryString(post_string); 

     } 

     public HttpNameValueCollection(ref HttpListenerContext ctx) 
     { 
      _ctx = ctx; 
      PopulatePost(); 
     } 


    } 
} 
+0

Si stanno conservando i dati dei file nel tipo di stringa. Stai attento, hai a che fare con il binario e devi considerare i file di grandi dimensioni. – fyasar