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#设计模式开发的基础教程

    代理模式是一种常见的设计模式,它允许我们通过代理对象来控制对另一个对象的访问。在C#中,代理模式可以用于许多场景,例如远程代理、虚拟代理、保护代理等。本文将提供使用代理模式进行C#设计模式开发的基础教程,帮助您了解代理模式的基本概念和用法。 代理模式的基本概念 代理模式是一种结构型设计模式,它允许我们通过代理对象来控制对另一个对象的访问。代理对象充当了另一个…

    C# 2023年5月15日
    00
  • c# 如何实现不同进程之间的通信

    下面是关于“C#如何实现不同进程之间的通信”的完整攻略,包含两个示例。 1. 什么是进程间通信 进程间通信(IPC)是指在不同进程之间传递数据或信号的机制。在C#中,我们可以使用多种方式实现进程间通信,例如命名管道、共享内存、消息队列等。 2. 示例1:使用命名管道实现进程间通信 以下是一个示例,演示如何使用命名管道实现进程间通信: // 服务端 using…

    C# 2023年5月15日
    00
  • C# 操作Excel代码总结

    下面就为您详细讲解“C# 操作Excel代码总结”的完整攻略。 一、前言 在日常开发中,Excel 是我们经常应用的工具。在 C# 中,操作 Excel 也是一个常见的需求。本文就通过两个示例,来总结一下 C# 中操作 Excel 的代码实现过程。 二、基本环境 在操作 Excel 的过程中,我们需要引入以下命名空间: using Microsoft.Off…

    C# 2023年6月7日
    00
  • 使用Linq注意事项避免报错的方法

    使用Linq时要注意以下几点,以避免在代码中出现错误: 1. 空引用异常 在使用Linq时,一定要注意空引用异常,这通常是因为查询结果为 null,或者结果集中的某些数据为 null。 解决此问题的方法是,先要用 null 检查语句来确保在使用结果集中的某些属性时,结果集不为空。可以使用 ?? 运算符来处理 null 异常。 以下是一个示例代码,可以用于处理…

    C# 2023年5月14日
    00
  • C#获取文件夹所占空间大小的功能

    要实现该功能,可以使用C#内置的System.IO命名空间下的DirectoryInfo类和FileInfo类。下面是详细的步骤: 使用DirectoryInfo类创建文件夹的实例。 DirectoryInfor dirInfo = new DirectoryInfo(@"C:\Users\Administrator\Desktop\Example…

    C# 2023年6月1日
    00
  • C#使用dynamic一行代码实现反射操作

    针对这个问题,我会给出一个详细的攻略和两个示例说明,希望对您有所帮助。 C#使用dynamic一行代码实现反射操作 在C#中,我们通常使用反射来访问和操作对象的成员,这样做需要费一些脑筋和代码量,但是我们可以通过使用dynamic类型来使得反射操作变得更为简便。 下面是使用dynamic一行代码实现反射操作的步骤: 创建一个动态类型的对象; 使用点号访问对象…

    C# 2023年5月31日
    00
  • C# 委托的三种调用示例(同步调用 异步调用 异步回调)

    C# 委托是一种特殊的数据类型,它允许在运行时将方法作为参数传递给其他方法,也可以作为返回值,这在异步编程中很有用。本篇攻略将重点讲解 C# 委托的三种调用示例:同步调用、异步调用和异步回调。 同步调用 同步调用是指调用一个方法时,程序会一直等待该方法执行完毕并返回结果后再继续执行下一步操作。这种调用方式是最常见的,也是最简单的方式。 以下代码示例展示了委托…

    C# 2023年6月1日
    00
  • C#算法之冒泡排序、插入排序、选择排序

    C#算法之冒泡排序、插入排序、选择排序 在学习C#算法的过程中,冒泡排序、插入排序、选择排序是最基础且常用的排序算法之一。这些排序算法可以对数组进行排序,使其按照升序或降序排列。 本文将详细讲解这三种排序算法的原理和实现步骤,并提供两个示例说明。 冒泡排序 冒泡排序是一种比较简单的排序算法,其基本思想是:将相邻的两个元素进行比较,如果前一个元素比后一个元素大…

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