C#如何自定义multipart/form-data的解析器
作者:张云勇 发布时间:2023-12-04 18:59:03
标签: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);
}
}
来源:https://blog.csdn.net/xiaoyong_net/article/details/107688427


猜你喜欢
- 本文实例讲述了C#键盘鼠标钩子的实现方法。分享给大家供大家参考。具体实现方法如下:using System;using System.Col
- 在学习操作系统这本书的时候,我们使用的是汤小丹老师的《计算机操作系统》接下来我将会使用java语言去实现内部代码。Swap指令最佳置换算法是
- 调用和回调机制在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种:1.同步调用同步调用是最基本并且最简
- 本文实例分析了java中成员变量与局部变量区别。分享给大家供大家参考。具体分析如下:成员变量:在这个类里定义的私有变量,属于这个类。创建以及
- session对象用于在会话范围内,记录每个客户端的访问状态,以便于跟踪每个客户端的操作状态,在会话存储的信息,在浏览器发出后续请求时可以获
- 在程序开发过程中,如何会使用键盘来完成所有的操作,会提高开发的速度。所以说,灵活的掌握并应用visual studio 的键盘快捷键非常重要
- 先说能用的究极解决方案,大家着急的直接复制走,以后想了解再过来看没有header,且所有Item的高度一致private fun getSc
- 系列目录 【已更新最新开发文章,点击查看详细】类似于以下场景,将表单中的用户信息(包含附件)上传到服务器并保存到数据库中,<form
- 前言本文主要介绍了关于JDK源码分析之String、StringBuilder和StringBuffer的相关内容,分享出来供大家参考学习,
- 什么是静态单例模式?静态单例模式(Static Singleton Pattern)是我在实践中总结的模式,主要解决的问题是在预先知道某依赖
- 需要读取excel数据转换成json数据,写了个测试功能,转换正常:JSON转换:org.json.jar 测试类:
- 问题描述平常用的是java8,最近在学习java的新特性。这就需要从java8往更高的java版本切换。由于还在使用java8,测试完新特性
- 前言本文主要介绍的是关于CentOS 7配置自定义JDK的方法教程,分享出来供大家参考学习,下面来一起看看详细的介绍:配置教程由于选择的是C
- 成员内部类1.定义成员内部类是直接定义在类中,不加任何修饰符的(特指不加static修饰的)的内部类,可以类比着成员变量来理解,如下面这个代
- SpringBoot版本:2.3.2.RELEASESpringBoot Data JPA版本:2.3.2.RELEASEJpaReposi
- 工厂方法模式的定义工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂
- 引言树形结构不论在生活中或者是开发中都是一种非常常见的结构,一个容器对象(如文件夹)下可以存放多种不同的叶子对象或者容器对象,容器对象与叶子
- 公司的以前的项目,看到使用了这个Android自带的倒计时控件Chronometer,现在整合了一下先看看效果:<Chronomete
- C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际
- springboot 异常与重定向在spring中,有两个重定向类型:301,永久性跳转302,暂时性跳转默认调用302。1.下面先通过一个