C#如何自定义multipart/form-data的解析器
作者:张云勇
这篇文章主要介绍了C#如何自定义multipart/form-data的解析器,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
使用WebSocketSharp自定义实现Web服务时,无法解析multipart/form-data请求的数据。
通过查找资料,采用以下方式实现multipart/form-data的解析器。
解析辅助类
using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; namespace YongFrame.Common.Utils { /// <summary> /// multipart/form-data的解析器 /// </summary> internal class HttpMultipartParser { /// <summary> /// 参数集合 /// </summary> public IDictionary<string, string> Parameters = new Dictionary<string, string>(); /// <summary> /// 上传文件部分参数 /// </summary> public string FilePartName { get; } /// <summary> /// 是否解析成功 /// </summary> public bool Success { get; private set; } /// <summary> /// 请求类型 /// </summary> public string ContentType { get; private set; } /// <summary> /// 上传的文件名 /// </summary> public string Filename { get; private set; } /// <summary> /// 上传的文件内容 /// </summary> public byte[] FileContents { get; private set; } /// <summary> /// 解析multipart/form-data格式的文件请求,默认编码为utf8 /// </summary> /// <param name="stream"></param> /// <param name="filePartName"></param> public HttpMultipartParser(Stream stream, string filePartName) { FilePartName = filePartName; Parse(stream, Encoding.UTF8); } /// <summary> /// 解析multipart/form-data格式的字符串 /// </summary> /// <param name="content"></param> public HttpMultipartParser(string content) { var array = Encoding.UTF8.GetBytes(content); var stream = new MemoryStream(array); Parse(stream, Encoding.UTF8); } /// <summary> /// 解析multipart/form-data格式的文件请求 /// </summary> /// <param name="stream"></param> /// <param name="encoding">编码</param> /// <param name="filePartName"></param> public HttpMultipartParser(Stream stream, Encoding encoding, string filePartName) { FilePartName = filePartName; Parse(stream, encoding); } private void Parse(Stream stream, Encoding encoding) { Success = false; var data = ToByteArray(stream); var content = encoding.GetString(data); var delimiterEndIndex = content.IndexOf("\r\n", StringComparison.Ordinal); if (delimiterEndIndex > -1) { var delimiter = content.Substring(0, content.IndexOf("\r\n", StringComparison.Ordinal)).Trim(); var sections = content.Split(new[] {delimiter}, StringSplitOptions.RemoveEmptyEntries); foreach (var s in sections) { if (s.Contains("Content-Disposition")) { var nameMatch = new Regex(@"(?<=name\=\"")(.*?)(?=\"")").Match(s); var name = nameMatch.Value.Trim().ToLower(); if (name == FilePartName && !string.IsNullOrEmpty(FilePartName)) { var re = new Regex(@"(?<=Content\-Type:)(.*?)(?=\r\n\r\n)"); var contentTypeMatch = re.Match(content); re = new Regex(@"(?<=filename\=\"")(.*?)(?=\"")"); var filenameMatch = re.Match(content); if (contentTypeMatch.Success && filenameMatch.Success) { ContentType = contentTypeMatch.Value.Trim(); Filename = filenameMatch.Value.Trim(); var startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length; var delimiterBytes = encoding.GetBytes("\r\n" + delimiter); var endIndex = IndexOf(data, delimiterBytes, startIndex); var contentLength = endIndex - startIndex; var fileData = new byte[contentLength]; Buffer.BlockCopy(data, startIndex, fileData, 0, contentLength); FileContents = fileData; } } else if (!string.IsNullOrWhiteSpace(name)) { var startIndex = nameMatch.Index + nameMatch.Length + "\r\n\r\n".Length; Parameters.Add(name, s.Substring(startIndex).TrimEnd('\r', '\n').Trim()); } } } if (FileContents != null || Parameters.Count != 0) { Success = true; } } } public static int IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex) { var index = 0; var startPos = Array.IndexOf(searchWithin, serachFor[0], startIndex); if (startPos != -1) { while (startPos + index < searchWithin.Length) { if (searchWithin[startPos + index] == serachFor[index]) { index++; if (index == serachFor.Length) { return startPos; } } else { startPos = Array.IndexOf(searchWithin, serachFor[0], startPos + index); if (startPos == -1) { return -1; } index = 0; } } } return -1; } public static byte[] ToByteArray(Stream stream) { var buffer = new byte[32768]; using (var ms = new MemoryStream()) { while (true) { var read = stream.Read(buffer, 0, buffer.Length); if (read <= 0) { return ms.ToArray(); } ms.Write(buffer, 0, read); } } } } }
调用示例
HttpMultipartParser parser = new HttpMultipartParser(paramData); if (!parser.Success) { result.Code = -1; result.Message = "请求数据格式不能正确"; return result; } if (!parser.Parameters.ContainsKey("optid") || parser.Parameters["optid"] == null || string.IsNullOrEmpty(parser.Parameters["optid"])) { result.Code = -1; result.Message = "用户名不能为空"; return result; }
public void Upload(Stream stream) { HttpMultipartParser parser = new HttpMultipartParser(stream, "image"); if (parser.Success) { string user = HttpUtility.UrlDecode(parser.Parameters["user"]); string title = HttpUtility.UrlDecode(parser.Parameters["title"]); // Save the file somewhere File.WriteAllBytes(FILE_PATH + title + FILE_EXT, parser.FileContents); } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。