C#如何自定义multipart/form-data的解析器

 更新时间:2022年06月09日 09:30:51   作者:张云勇  
这篇文章主要介绍了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);
    }
}
 

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • spreadsheetgear插件屏蔽鼠标右键的方法

    spreadsheetgear插件屏蔽鼠标右键的方法

    今天用到spreadsheetGear插件,然后右键有插件自己的菜单。都是英文的,而且还能打开新的窗体。嵌到程序里面,不太合适,所以着手屏蔽
    2014-02-02
  • C#中使用Override和New关键字进行版本控制

    C#中使用Override和New关键字进行版本控制

    在 C# 中,override 和 new 关键字用于控制类之间的成员方法的隐藏和重写,理解它们之间的差异和使用场景对于设计灵活且易于维护的代码至关重要,在这篇博客中,我们将详细探讨这两个关键字的用法,并通过示例来说明它们的实际应用,需要的朋友可以参考下
    2024-10-10
  • C#中Forms.Timer、Timers.Timer、Threading.Timer的用法分析

    C#中Forms.Timer、Timers.Timer、Threading.Timer的用法分析

    这篇文章主要介绍了C#中Forms.Timer、Timers.Timer、Threading.Timer的用法分析,以实例形式较为详细的讲述了.NET Framework里面提供的三种Timer具体用法,需要的朋友可以参考下
    2014-10-10
  • C# winform 模拟键盘输入自动接入访问网络的实例

    C# winform 模拟键盘输入自动接入访问网络的实例

    本篇文章主要介绍了C# winform 模拟键盘输入自动接入访问网络,有兴趣的可以了解一下。
    2016-11-11
  • C#制作二维柱状图方法

    C#制作二维柱状图方法

    在本文里小编为各位分享的是关于C#制作二维柱状图方法和步骤,需要的读者们学习下。
    2018-12-12
  • c#编写webservice服务引用实例分享

    c#编写webservice服务引用实例分享

    c#编写webservice服务引用实例分享,大家参考使用吧
    2013-12-12
  • Unity 百度AI实现Logo商标识别

    Unity 百度AI实现Logo商标识别

    本文主要介绍了Unity实现检测和识别图片中的品牌LOGO信息。即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片中LOGO的名称、位置和置信度。需要的可以参考一下
    2022-01-01
  • C#中异步回调函数用法实例

    C#中异步回调函数用法实例

    这篇文章主要介绍了C#中异步回调函数用法,实例分析了异步回调函数的定义及使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-04-04
  • C#实现用于操作wav声音文件的类实例

    C#实现用于操作wav声音文件的类实例

    这篇文章主要介绍了C#实现用于操作wav声音文件的类,实例分析了C#操作wav音频文件的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-03-03
  • C#制作网站挂机程序的实现示例

    C#制作网站挂机程序的实现示例

    本文主要介绍了C#制作网站挂机程序,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10

最新评论