C#记一次http协议multipart/form-data的boundary问题

下面是“C#记一次http协议multipart/form-data的boundary问题”的完整攻略。

1. 问题背景

在使用 C# 发送 HTTP 请求时,如果请求体采用 multipart/form-data 格式,则需要在请求头和请求体中添加对应 "Content-Type""Boundary"。其中 "Boundary" 是分割每个 form-data 的标识字符。

常见的发送 multipart/form-data 请求体的方式为利用 HttpWebRequest 类发送 POST 请求。

例如,我们需要向服务器发送以下数据:

{ 
    "username": "huangzy", 
    "avatar": 二进制图片数据
}

我们可以使用以下 C# 代码来实现:

var boundary = "------------------------" + DateTime.Now.Ticks.ToString("x");
var formData = "--" + boundary + "\r\n" +
               "Content-Disposition: form-data; name=\"username\"" + "\r\n\r\n" + 
               "huangzy" + "\r\n" +
               "--" + boundary + "\r\n" + 
               "Content-Disposition: form-data; name=\"avatar\"; filename=\"avatar.jpg\"" + "\r\n" + 
               "Content-Type: image/jpeg" + "\r\n\r\n" + 
               // 二进制图片数据 + "\r\n" +
               "--" + boundary + "--" + "\r\n";

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://example.com/api/upload");
request.Method = "POST";
request.ContentType = "multipart/form-data; boundary=" + boundary;

using (Stream stream = request.GetRequestStream())
{
    byte[] formDataBytes = Encoding.UTF8.GetBytes(formData);
    stream.Write(formDataBytes, 0, formDataBytes.Length);

    // 写入二进制数据流...
}

HttpWebResponse response = (HttpWebResponse)request.GetResponse();

在以上代码中,我们在请求头中添加了 "Content-Type": "multipart/form-data; boundary=xxx",其中 "boundary" 是分割各个 form-data 数据的标识字符串。在请求体中,我们按照 "boundary" 的格式拼凑了 form-data 数据。

2. 问题分析

在上述代码中,我们拼凑 "boundary" 的方式如下:

var boundary = "------------------------" + DateTime.Now.Ticks.ToString("x");

这种方式只会在当前请求中生成一个随机的 "boundary",如果我们需要循环发送多个请求,则会出现问题。

例如以下场景:我们需要向服务器发送 3 个文件,每个文件大小不超过 1MB。这种情况下,我们不能将所有的文件都存储在内存中,而需要每传输一部分数据,就立即释放内存。因此我们需要分别对每个文件发送一个 HTTP 请求。这时,我们的代码可能是这样的:

foreach (var file in files)
{
    var boundary = "------------------------" + DateTime.Now.Ticks.ToString("x");
    var formData = "--" + boundary + "\r\n" +
                   "Content-Disposition: form-data; name=\"file\"" + "\r\n" +
                   "Content-Type: application/octet-stream" + "\r\n\r\n";

    formData += // 添加文件二进制数据...;

    formData += "\r\n--" + boundary + "--\r\n";

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://example.com/api/upload");
    request.Method = "POST";
    request.ContentType = "multipart/form-data; boundary=" + boundary;

    using (Stream stream = request.GetRequestStream())
    {
        byte[] formDataBytes = Encoding.UTF8.GetBytes(formData);
        stream.Write(formDataBytes, 0, formDataBytes.Length);

        // 写入文件二进制数据...
    }

    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
}

在以上代码的循环中,每次生成的 "boundary" 均不同。因此,发送的请求中 "boundary" 也都不同。如果服务器端无法正确解析出请求体中的 "boundary",就会返回错误的响应。

3. 解决方案

为了解决上述问题,我们需要确定请求中的 "boundary" 值。

对于没有重复文件名的数据发送,我们可以使用以下方式:

var boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");

"boundary" 的值与上一种方式类似,只是去掉了 "-" 的个数,防止出现 "boundary" 与请求体内容重叠的情况。

对于重复文件名的情况,我们需要特别处理。由于不同的文件大小不同,如果直接使用同一个 "boundary",将可能返回错误的响应。

为了解决该问题,我们需要使用文件名 + 文件大小作为 "boundary" 的值:

var byteArray = File.ReadAllBytes(file); // 获取文件二进制数据
var stream = new MemoryStream(byteArray);
var boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");

var formData = "--" + boundary + "\r\n" +
               "Content-Disposition: form-data; name=\"file\"; filename=\"" + Path.GetFileName(file) + "\"\r\n" +
               "Content-Type: application/octet-stream\r\n\r\n";

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://example.com/api/upload");
request.Method = "POST";
request.ContentType = "multipart/form-data; boundary=" + boundary;

using (var reqStream = request.GetRequestStream())
{
    var formDataBytes = Encoding.UTF8.GetBytes(formData);
    reqStream.Write(formDataBytes, 0, formDataBytes.Length);

    var buffer = new byte[4096];
    int bytesRead = 0;
    while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
    {
        reqStream.Write(buffer, 0, bytesRead);
    }

    var end = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
    reqStream.Write(end, 0, end.Length);
}

HttpWebResponse response = (HttpWebResponse)request.GetResponse();

对于以上代码,我们将文件名 + 文件大小作为 "boundary" 的值,避免了同一个 "boundary" 重复使用的情况。

以上就是针对 C# 中发送 multipart/form-data"boundary" 问题的完整攻略。

4. 示例

下面我提供两个示例,分别是:

  • 发送不同文件的 multipart/form-data 请求
  • 发送同一文件的 multipart/form-data 请求

示例1:发送不同文件的 multipart/form-data 请求

以下是示例代码:

// 我们需要发送的文件列表
var files = new List<string>
{
    "/path/to/file1.txt",
    "/path/to/file2.jpg",
    "/path/to/file3.png"
};

// boundary 字符串
var boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");

// 循环发送每个文件
foreach (var file in files)
{
    // 初始化 HttpWebRequest 对象
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://example.com/api/upload");
    request.Method = "POST";
    request.ContentType = "multipart/form-data; boundary=" + boundary;

    // 读取文件数据
    byte[] byteArray = File.ReadAllBytes(file);

    // 构造请求体
    string header = "--" + boundary + Environment.NewLine +
                    "Content-Disposition: form-data; name=\"file\"; filename=\"" + Path.GetFileName(file) + "\"" + Environment.NewLine +
                    "Content-Type: application/octet-stream" + Environment.NewLine +
                    Environment.NewLine;
    string footer = Environment.NewLine + "--" + boundary + "--" + Environment.NewLine;

    byte[] headerBytes = Encoding.UTF8.GetBytes(header);
    byte[] footerBytes = Encoding.UTF8.GetBytes(footer);

    using (Stream stream = request.GetRequestStream())
    {
        stream.Write(headerBytes, 0, headerBytes.Length);
        stream.Write(byteArray, 0, byteArray.Length);
        stream.Write(footerBytes, 0, footerBytes.Length);
    }

    // 发送请求并接收响应
    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
    {
        // TODO 处理响应
    }
}

在以上代码中,我们循环发送每个文件,每个请求独立生成 "boundary" 并构造请求体。

示例2:发送同一文件的 multipart/form-data 请求

以下是示例代码:

// 文件路径
var file = "/path/to/same/file.txt";

// 获取文件数据
byte[] byteArray = File.ReadAllBytes(file);

// boundary 字符串,由文件名 + 文件大小组成
var boundary = "----------------------------" + file.Length + "_" + DateTime.Now.Ticks.ToString("x");

// 初始化 HttpWebRequest 对象
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://example.com/api/upload");
request.Method = "POST";
request.ContentType = "multipart/form-data; boundary=" + boundary;

// 构造请求体
string header = "--" + boundary + Environment.NewLine +
                "Content-Disposition: form-data; name=\"file\"; filename=\"" + Path.GetFileName(file) + "\"" + Environment.NewLine +
                "Content-Type: application/octet-stream" + Environment.NewLine +
                Environment.NewLine;
string footer = Environment.NewLine + "--" + boundary + "--" + Environment.NewLine;

byte[] headerBytes = Encoding.UTF8.GetBytes(header);
byte[] footerBytes = Encoding.UTF8.GetBytes(footer);

using (Stream stream = request.GetRequestStream())
{
    stream.Write(headerBytes, 0, headerBytes.Length);
    stream.Write(byteArray, 0, byteArray.Length);
    stream.Write(footerBytes, 0, footerBytes.Length);
}

// 发送请求并接收响应
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
    // TODO 处理响应
}

在以上代码中,我们使用文件名 + 文件大小作为 "boundary" 值,并循环发送请求。在这个过程中,"boundary" 的值不变,避免了发送请求时值不一致的问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#记一次http协议multipart/form-data的boundary问题 - Python技术站

(0)
上一篇 2023年5月15日
下一篇 2023年5月15日

相关文章

  • c#与WMI使用技巧集第1/2页

    c#与WMI使用技巧集第1/2页是一篇介绍C#与WMI使用技巧的文章,主要包括WMI的基础知识、C#中如何使用WMI等方面的内容。以下是该文章完整攻略的详细讲解: WMI基础知识 WMI(Windows Management Instrumentation)是一种用于管理Windows操作系统的工具,可以用于获取系统信息、监控、配置等。在C#中使用WMI可以…

    C# 2023年6月6日
    00
  • 详解ASP.NET Core 处理 404 Not Found

    详解ASP.NET Core处理404 Not Found攻略 在ASP.NET Core应用程序中,当用户请求一个不存在的资源时,服务器将返回404 Not Found错误。本攻略将介绍如何在ASP.NET Core应用程序中处理404 Not Found错误。 步骤 以下是处理404 Not Found错误的步骤: 添加UseStatusCodePage…

    C# 2023年5月17日
    00
  • 详解C#如何实现读写ini文件

    下面就详细讲解一下如何在C#中读写ini文件。 什么是ini文件 ini是一种配置文件,它是Windows操作系统中常用的一种配置文件格式,常用于存放程序的配置信息,如窗口大小、颜色等。 读取ini文件 1. 使用Win32 API C#可以通过调用Win32 API函数读取ini文件。主要函数有GetPrivateProfileString和GetPriv…

    C# 2023年5月15日
    00
  • 浅谈从ASP.NET Core2.2到3.0你可能会遇到这些问题

    浅谈从ASP.NET Core 2.2到3.0你可能会遇到这些问题 ASP.NET Core 3.0是一个重大的版本更新,其中包含了许多新功能和改进。在本攻略中,我们将讨论从ASP.NET Core 2.2升级到3.0时可能会遇到的一些常见问题,并提供一些解决方案。 问题一:MVC应用程序中的路由不起作用 在ASP.NET Core 3.0中,MVC应用程序…

    C# 2023年5月17日
    00
  • ASP.NET Core  依赖注入框架的使用

    ASP.NET Core 依赖注入框架的使用攻略 1. 什么是依赖注入? 依赖注入是一种设计模式,它能够解决对象之间的依赖关系。它主要是通过将对象的依赖关系交给外部容器来管理,从而实现将对象之间的耦合度降低。 2. 为什么要使用依赖注入? 使用依赖注入可以带来以下一些好处: 使应用程序更易于测试。 降低对象间的耦合度,使得应用程序更容易扩展和维护。 可以更好…

    C# 2023年6月3日
    00
  • C#使用HttpHelper框架重启路由器

    我们一步一步来。 概述 要使用HttpHelper框架重启路由器,我们需要先了解什么是HttpHelper框架和路由器重启的过程。 HttpHelper框架是一个基于.NET Framework的HTTP请求框架,可以帮助我们实现请求数据、提交数据、模拟浏览器,进而实现网络爬虫等多种功能。而路由器重启,则是通过模拟客户端向路由器发送重启指令,实现路由器远程重…

    C# 2023年6月3日
    00
  • .NET使用Collections.Pooled提升性能优化的方法

    .NET使用Collections.Pooled提升性能优化的方法 简述 在进行 .NET 开发过程中,尤其在一些高并发、大量数据操作的场景下,很容易出现内存泄漏和性能问题。而使用 C# 中的 Collections.Pooled 可以有效地缓解此类问题,从而提高程序的性能。本文将详细介绍 Collections.Pooled 的使用方法及优化效果。 Col…

    C# 2023年6月3日
    00
  • C#通过HttpWebRequest发送带有JSON Body的POST请求实现

    C#通过HttpWebRequest发送带有JSON Body的POST请求实现,一般包括以下几个步骤: 1. 引入命名空间 在 C# 文件中,需要引入 System.Net 命名空间,代码如下: using System.Net; 2. 创建 POST 请求对象 使用 HttpWebRequest 类创建 POST 请求对象,代码如下: HttpWebRe…

    C# 2023年5月31日
    00
合作推广
合作推广
分享本页
返回顶部