Httplistener和文件上传

我正在尝试从我的网络服务器检索上传的文件。 当客户端通过webform(随机文件)发送文件时,我需要解析请求以获取文件并进一步处理它。 基本上,代码如下:

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 

因此,我能够检索文本文件,但对于所有其他文件,它们已损坏。 有人能告诉我如何正确解析这些HttplistnerRequests(或提供轻量级替代)?

我认为你通过使用HttpListener而不是使用ASP.Net的内置工具来使自己的事情变得更加困难。 但是如果你必须这样做,这里有一些示例代码。 注意:1)我假设您在

上使用enctype="multipart/form-data" 。 2)此代码旨在与仅包含的表单一起使用,如果您要发布其他字段或多个文件,则必须更改代码。 3)这是概念/示例的certificate,它可能有错误,并且不是特别灵活。

 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标头指定了主体的总长度,包括边界,子标题和间距)。

问题是您正在以文本forms阅读文件。

您需要将文件读取为bytearray而使用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); 

可能有虫子,彻底测试。 这个获取所有发布,获取和文件。

 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 _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 Files { get { return _files ?? (_files = new Dictionary()); } 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(); 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(); } } }