2011-12-11 67 views
11

我想從我的網絡服務器檢索上傳的文件。當客戶端通過webform(隨機文件)發送文件時,我需要解析請求以將文件取出並進一步處理。 基本上,代碼都爲:Httplistener和文件上傳

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 

其結果是,我能夠檢索TEXTFILES,但對於所有其他文件,這些文件已損壞。 有人可以告訴我如何正確解析這些HttplistnerRequests(或提供一個輕量級的選擇)?

+0

我想你會在你的web形式使用ENCTYPE =「的multipart/form-data的」?如果是這樣,它看起來像你的簡單化方式 –

回答

13

我認爲你對自己做的事情更難比必要通過與這樣HttpListener而不是使用ASP.Net的內置工具。但是,如果你必須這樣做,這裏是一些示例代碼。注意:1)我假設您在<form>上使用enctype="multipart/form-data"。 2)此代碼旨在與僅包含您的<input type="file" />的表單一起使用,如果您想發佈其他字段或多個文件,則必須更改代碼。 3)這是爲了證明概念/例子,它可能有錯誤,並且不是特別靈活。

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

爲了幫助您更好地瞭解上面的代碼是幹什麼的,這裏是什麼HTTP POST的身體看起來像:

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

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

Test 
------WebKitFormBoundary9lcB0OZVXSqZLbmv-- 

我已經離開了不相干的頭。如您所見,您需要通過掃描來查找正文和結束邊界序列,然後刪除文件內容之前的子標題。不幸的是,由於二進制數據的潛力,你不能使用StreamReader。另外不幸的是,每個文件都沒有Content-Length(請求的Content-Length頭部指定了包括邊界,子頭文件和間距在內的主體的總長度。

+0

我在JPEG和文本文件上測試過這兩種方法。 –

+0

你好,謝謝你的詳細回覆!它的確工作並且幫助我很多! (即使這不是最有效的方式) – cecemel

+0

這就是我要找的。謝謝 – fyasar

1

問題是您正在以文本形式讀取文件。

您需要閱讀該文件作爲一個字節組來代替,而使用BinaryReader是更好,更容易使用比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

嗨,謝謝你的回覆! 看來不過request.putStream.Length提供了一個不支持的異常。 此外,bytearray使工作複雜化,因爲有一些解析需要要從其他主體內容中提取文件。 Streamrider似乎從這個角度來看更適合用途。 – cecemel

+0

@ user1092608:哦,那很不幸。問題在於,您無法真正使用流讀取器的文本讀取功能來提取二進制文件。您是否使用FileUpload控件或其他方法將文件嵌入到流中? –

+0

這是一個相關的評論。 現在客戶端基本上使用默認的html form。如果可能的話,我想避免使用fileUpload控件,以儘可能簡化事情 - 無論如何,這仍然意味着 - 。 在這個項目中,另一個實例(機器)應該能夠在我的服務器上隨機發送文件以及一些元數據,用它來做東西並將結果發回。 – cecemel

1

可能存在缺陷,徹底測試這一個獲得所有職位,獲取和文件。

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

您正在以字符串類型保存文件數據。要小心,你正在處理二進制文件,你必須考慮大文件。 – fyasar