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日

相关文章

  • 如何在ASP.NET Core类库项目中读取配置文件详解

    如何在ASP.NET Core类库项目中读取配置文件详解 在ASP.NET Core中,读取配置文件是非常常见的操作。本攻略将提供详细的步骤和示例说明,演示如何在ASP.NET Core类库项目中读取配置文件。 步骤 步骤1:创建一个新的ASP.NET Core类库项目 首先,需要创建一个新的ASP.NET Core类库项目。可以使用以下命令在命令行中创建一…

    C# 2023年5月17日
    00
  • 什么是机器视觉?

    由于当前社会人力成本越来越昂贵,机器取代人力是大势所趋,自动化的发展也随之越来越快 。当制造公司需 要一双手和一对 眼睛的时候却不得不雇佣一个人的苦恼日益加重,而传统的机器设计和电气自动化的发展,解决一双手的问题已经渐渐得到了缓解,现在就到了需要解决一双眼睛的时候,机器视觉的出现和广泛应用也随着到来。 机器视觉是计算机科学的一个重要分支,它综合了光学,机械,…

    C# 2023年4月19日
    00
  • Unity中协程IEnumerator的使用方法介绍详解

    针对“Unity中协程IEnumerator的使用方法介绍详解”这个话题,以下是详细的攻略: 什么是协程? 协程是一个非常重要的Unity中的功能,它可以让你在程序执行期间暂停执行当前方法,进行一段时间的等待,然后再继续执行这个方法。通过协程,你可以创建更加动态、流畅的游戏体验。 协程的使用方法 在Unity中,协程的使用方法非常简单,我们只需要使用IEnu…

    C# 2023年6月3日
    00
  • C#使用DirectX.DirectSound播放语音

    下面我就详细讲解一下C#使用DirectX.DirectSound播放语音的完整攻略。 1.准备工作 在开始使用DirectX.DirectSound播放语音之前,需要先安装Microsoft DirectX SDK,并将其添加到工程引用中。 2.创建DirectSound实例 使用DirectX.DirectSound播放语音的第一步是创建DirectSo…

    C# 2023年6月6日
    00
  • asp.net(c#)开发中的文件上传组件uploadify的使用方法(带进度条)

    下面我将为您详细讲解asp.net(c#)开发中文件上传组件uploadify的使用方法(带进度条)的完整攻略。 一. 简介 uploadify是一款基于jQuery的文件上传插件,支持多文件上传,支持进度条显示。 二. 安装与引入 下载uploadify:在官网 https://www.uploadify.com/ 下载uploadify并解压文件。 引入…

    C# 2023年6月1日
    00
  • C#影院售票系统毕业设计(3)

    “C#影院售票系统毕业设计(3)”提供了影院售票系统的完整设计和开发流程。以下是攻略的详细讲解: 1. 设计数据库 在设计影院售票系统之前,需要对数据库进行设计。可以使用SQL Server Management Studio创建一个名为MovieTicket的数据库,并在其中创建3个表格:Movie(电影)、Hall(影厅)和Ticket(票务信息)。 可…

    C# 2023年6月7日
    00
  • 如何在C#项目中链接一个文件夹下的所有文件详解

    当我们需要在C#项目中链接一个文件夹下的所有文件时,可以通过以下方式实现: 在 Visual Studio 中创建 C# 项目。选择 “File” > “New” > “Project”,然后选择 “Visual C#” > “Windows” > “Console Application”。 在项目中添加文件夹。右键项目,选择 “A…

    C# 2023年6月1日
    00
  • C#实现只运行单个实例应用程序的方法(使用VB.Net的IsSingleInstance)

    实现只运行单个实例应用程序的方法,在C#中可以通过使用Mutex实现。Mutex是一种用于互斥访问共享资源的同步基元。在应用程序的运行过程中,只允许存在一个互斥体。如果进程试图创建同名的互斥体,则只能打开已存在的同名互斥体,而不是创建一个新的互斥体。 下面是实现只运行单个实例应用程序的方法的代码片段: using System.Threading; // 定…

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