您的位置:首页 > 理论基础 > 计算机网络

C#利用HttpListener实现接受上传文件

2017-07-26 17:12 435 查看
最近一个winform项目想直接用http协议与java端进行双向通讯,发现HttpListener可以实现http通讯,如果提交form的enctype=application/x-www-form-urlencoded则可以通过HttpUtility.ParseQueryString来解析HttpListenerRequest.InputStream中的内容,但如果是multipart/form-data,没有找到可以直接解析的类。

在网上找了半天,发现了http://www.xuebuyuan.com/464241.html,可以实现解析上传文件,不过处理一个1M的上传表单,竟然要8秒,性能实在无法接受(后来发现瓶颈主要在流读取和数组读取的速度差异)。

想着dotnet内部肯定有实现类,在用reflection反复查找后,终于确定HttpMultipartContentTemplateParser是可以用来解析MFC1867协议的,但由于是内部类无法在外部实现,故将代码整理如下。

/*
RFC1867协议 举例
------WebKitFormBoundaryKcbJOyftlttL1JBB
Content-Disposition: form-data; name="name"

zq
------WebKitFormBoundaryKcbJOyftlttL1JBB
Content-Disposition: form-data; name="myfile"; filename="IMG_20130219_181308.jpg"
Content-Type: image/jpeg
[二进制数据]

------WebKitFormBoundaryKcbJOyftlttL1JBB--
*/

/// <summary>
/// 解析RFC1867协议
/// </summary>
public class HttpMultipartFormParser {
private string boundary;
private byte[] _boundary;
private byte[] _data;
private int _length;
private int _lineLength = -1;
private int _lineStart = -1;
private int _pos;
private bool _lastBoundaryFound;
private string _partContentType;
private int _partDataLength = -1;
private int _partDataStart = -1;
private string _partFilename;
private string _partName;
private Encoding _encoding;

public HttpMultipartFormParser(HttpListenerRequest request, Encoding encoding) {
this._encoding = encoding;
//Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Regex regex = new Regex("boundary=(.*)$");

Match match = regex.Match(request.ContentType);

if (match.Success) {
boundary = match.Groups[1].Value;
_boundary = _encoding.GetBytes("--" + boundary);
}

Stream input = request.InputStream;

//将上传文件保存到内存
BufferedStream br = new BufferedStream(input);

MemoryStream ms = new MemoryStream();

byte[] buffer = new byte[4096];

int len = 0;

while ((len = br.Read(buffer, 0, buffer.Length))>0) {
ms.Write(buffer,0,len);
}

_data = ms.ToArray();

_length = _data.Length;

ms.Close();
}

/// <summary>
/// 获取每一行数据
/// </summary>
/// <returns></returns>
private bool GetNextLine() {
int num = this._pos;

this._lineStart = -1;

while (num < this._length) {
if (this._data[num] == 10) { // '\n'
this._lineStart = this._pos;
this._lineLength = num - this._pos;
this._pos = num + 1;

// ignore \r
if ((this._lineLength > 0) && (this._data[num - 1] == 13)) {
this._lineLength--;
}

break;
}

if (++num == this._length) {
this._lineStart = this._pos;
this._lineLength = num - this._pos;
this._pos = this._length;
}
}

return (this._lineStart >= 0);
}

/// <summary>
/// 当前行是否是分隔符行
/// </summary>
/// <returns></returns>
private bool AtBoundaryLine() {
int length = this._boundary.Length;

if ((this._lineLength != length) && (this._lineLength != (length + 2))) {
return false;
}

for (int i = 0; i < length; i++) {
if (this._data[this._lineStart + i] != this._boundary[i]) {
return false;
}
}

if (this._lineLength != length) {
// last boundary line? (has to end with "--")
if ((this._data[this._lineStart + length] != 0x2d) || (this._data[(this._lineStart + length) + 1] != 0x2d)) {
return false;
}

this._lastBoundaryFound = true;
}

return true;
}

/// <summary>
/// 是否解析完毕
/// </summary>
/// <returns></returns>
private bool AtEndOfData() {
if (this._pos < this._length) {
return this._lastBoundaryFound;
}

return true;
}

/// <summary>
/// 从Content-Disposition:行抽取""中的内容
/// </summary>
/// <param name="l">行内容</param>
/// <param name="pos"></param>
/// <param name="name"></param>
/// <returns></returns>
private string ExtractValueFromContentDispositionHeader(string l, int pos, string name) {
String pattern = name + "=\"";

//:所在行位置+1
int i1 = CultureInfo.InvariantCulture.CompareInfo.IndexOf(l, pattern, pos, CompareOptions.IgnoreCase);

if (i1 < 0)
return null;
i1 += pattern.Length;

int i2 = l.IndexOf('"', i1);
if (i2 < 0)
return null;
if (i2 == i1)
return String.Empty;

return l.Substring(i1, i2 - i1);
}

/// <summary>
/// 读取头部信息
/// </summary>
private void ParsePartHeaders() {
_partName = null;
_partFilename = null;
_partContentType = null;

while (GetNextLine()) {
if (_lineLength == 0)
break;  // empty line signals end of headers ->\r\n

// get line as String
byte[] lineBytes = new byte[_lineLength];

Array.Copy(_data, _lineStart, lineBytes, 0, _lineLength);

String line = _encoding.GetString(lineBytes);

// parse into header and value
int ic = line.IndexOf(':');
if (ic < 0)
continue;   // not a header

// remeber header
String header = line.Substring(0, ic);

if (header.Equals("Content-Disposition")) {
// parse name and filename
_partName = ExtractValueFromContentDispositionHeader(line, ic + 1, "name");
_partFilename = ExtractValueFromContentDispositionHeader(line, ic + 1, "filename");
}
else if (header.Equals("Content-Type")) {
_partContentType = line.Substring(ic + 1).Trim();
}
}
}

/// <summary>
/// 处理数据部分
/// </summary>
private void ParsePartData() {
_partDataStart = _pos;
_partDataLength = -1;

while (GetNextLine()) {
if (AtBoundaryLine()) {
// calc length: adjust to exclude [\r]\n before the separator
int iEnd = _lineStart - 1;
if (_data[iEnd] == 10)   // \n
iEnd--;
if (_data[iEnd] == 13)   // \r
iEnd--;

_partDataLength = iEnd - _partDataStart + 1;
break;
}
}
}

/// <summary>
/// 解析数据为对象列表
/// </summary>
/// <returns></returns>
public List<MultipartFormItem> ParseIntoElementList() {
List<MultipartFormItem> itemList = new List<MultipartFormItem>();

while (GetNextLine()) {
if (AtBoundaryLine())
break;
}

if (AtEndOfData())
return itemList;

do {
// Parse current part's headers
ParsePartHeaders();

if (AtEndOfData())
break;          // cannot stop after headers

// Parse current part's data
ParsePartData();

if (_partDataLength == -1)
break;          // ending boundary not found

// Remember the current part (if named)
if (_partName != null) {
MultipartFormItem item = new MultipartFormItem();
item.Name = _partName;
item.Data = new byte[_partDataLength];

Buffer.BlockCopy(_data, _partDataStart, item.Data, 0, _partDataLength);

item.ContentType = _partContentType;

if (item.ContentType != null) {
item.ItemType = FormItemType.File;
}

itemList.Add(item);
}
}
while (!AtEndOfData());

return itemList;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  RFC1867 http协议 c#