2011-12-11 36 views
11

Yüklenen bir dosyayı web sunucumdan almaya çalışıyorum. İstemci dosyalarını bir web formu (rastgele dosyalar) aracılığıyla gönderirken, dosyayı almak ve daha fazlasını işlemek için isteği çözümlemem gerekir. Sonuç olarak, ben TextFiles almak mümkün duyuyorumHttplistener ve dosya yükleme

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 

, ancak diğer tüm dosyalar için, bunlar bozuk: olarak Temelde, kod gider. Birisi bu HttplistnerRequest'leri doğru şekilde nasıl ayrıştırılacağını (veya hafif bir alternatif sağladığını) söyleyebilir mi?

+0

yolu basitleştiriyorsun gibi pek görünüyor ben? web formunda enctype = "multipart/form-data" kullandığınız götürün İçeriği okuyorsunuz –

cevap

13

ASP.Net'in yerleşik tesislerini kullanmak yerine HttpListener. Ama bunu yapman gerekiyorsa, buradaki örnek bir koddur. Not: 1) <form> ürününüzde enctype="multipart/form-data" kullanıyorsunuz. 2) Bu kod sadece <input type="file" />'u içeren bir formla kullanılmak üzere tasarlanmıştır, eğer diğer alanları veya birden fazla dosyayı göndermek istiyorsanız kodu değiştirmeniz gerekecektir. 3) Bu kavram/örnek kanıtı, hatalar olabilir ve özellikle esnek değildir.

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

burada, daha iyi Yukarıdaki kod ne yaptığını anlamasına yardımcı gibi HTTP POST gövdesi göründüğünü olduğunu için: Alakasız başlıklarını bıraktım

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

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

Test 
------WebKitFormBoundary9lcB0OZVXSqZLbmv-- 

. Gördüğünüz gibi, başlangıç ​​ve bitiş sınır dizilerini bulmak için taramak suretiyle vücudunuzu ayrıştırmanız ve dosyanızın içeriğinden önce gelen alt başlıkları daraltmanız gerekir. Ne yazık ki, StreamReader'ı ikili veri potansiyeli nedeniyle kullanamazsınız. Ayrıca talihsiz herhangi bir dosya Content-Length (istek için Content-Length üstbilgisi yoktur) gerçeği sınırlarını, alt başlıkları ve boşlukları da dahil olmak üzere vücudun toplam uzunluğunu belirtir.

+0

Bunu bir JPEG ve bir metin dosyası üzerinde denedim ve her ikisi de çalıştı. –

+0

Merhaba, bu ayrıntılı yanıt için teşekkürler! Gerçekten çalışıyor ve bana çok yardımcı oluyor! (Bu en verimli yol olmasa bile) – cecemel

+0

Ne aradığımı. Teşekkürler – fyasar

1

Sorun şu anda dosyayı metin olarak okuyorsunuz.

Onun yerine bir ByteArray dosyasını okumak gerekir ve BinaryReader kullanarak daha iyi ve daha kolay StreamReader daha kullanmaktır: bir ile yaparak sana gerekli olandan kendin zor şeyler yapıyoruz düşünüyorum

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

Merhaba, cevabınız için teşekkürler! Ancak, request.InputStream.Length desteklenmeyen bir istisna sağlar.Ayrıca, bytearray ihtiyacı olan bazı ayrıştırma olduğundan işi karmaşıklaştırır dosyayı diğer vücut içeriğinden çıkarmak için yer alır. Streamrider bu bakış açısından amaç için daha uygun görünüyordu. – cecemel

+0

@ user1092608: Oh, bu talihsiz bir şey. Sorun şu ki, bir ikili dosya ayıklamak için akış okuyucusunun okuma becerilerini gerçekten kullanamazsınız. Dosyayı akışa gömmek için bir FileUpload denetimi mi yoksa başka bir yöntem mi kullanıyorsunuz? –

+0

Bu uygun bir sözdür. Şu anda istemci temelde varsayılan html formunu kullanır. Mümkünse, dosyaları mümkün olduğunca basit tutmak için FileUpload denetiminden kaçınmak isterim. Bu projede, başka bir örnek (makine), bazı meta verilerle birlikte sunucumda rasgele dosyaları atabiliyor ve bunlarla ilgili işleri yapabilmeli ve sonuçları geri gönderebilmelidir. – cecemel

1

Hatalar olabilir, iyice test edebilirsiniz . bunun her mesaj, almak ve dosyalar alır.

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

Dosya verilerini string tipinde tutuyorsunuz. Dikkatli olun, ikili ile uğraşıyorsunuz ve büyük dosyaları düşünmelisiniz. – fyasar