我正在使用C#,正在编写的2个应用程序之间进行一些通信。我开始喜欢Web API和JSON。现在,我正在编写例程以在两个服务器之间发送包含一些文本数据和文件的记录。
根据互联网,我应该使用multipart / form-data请求,如下所示:
基本上,您手动编写一个遵循如下格式的请求:
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
这种格式对于习惯于使用JSON数据的人来说非常令人不快。因此,很明显,解决方案是创建一个JSON请求,并对文件进行Base64编码,并最终得到如下请求:
{
"field1":"Joe Blow",
"fileImage":"JVBERi0xLjUKJe..."
}
我们可以在任何需要的地方使用JSON序列化和反序列化。最重要的是,发送此数据的代码非常简单。您只需为JSON序列化创建类,然后设置属性即可。文件字符串属性的设置很简单:
using (FileStream fs = File.Open(file_path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] file_bytes = new byte[fs.Length];
fs.Read(file_bytes, 0, file_bytes.Length);
MyJsonObj.fileImage = Convert.ToBase64String(file_bytes);
}
每个项目都不再有愚蠢的分隔符和标头。现在剩下的问题是性能。因此,我对此进行了介绍。我有一组50个样本文件,需要通过电线发送,范围从50KB到1.5MB左右。首先,我写了几行代码,简单地将文件流式传输到字节数组,以将其与文件流式传输的逻辑进行比较,然后将其转换为Base64流。以下是我介绍的2个代码块:
直接流分析多部分/表单数据
var timer = new Stopwatch();
timer.Start();
using (FileStream fs = File.Open(file_path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] test_data = new byte[fs.Length];
fs.Read(test_data, 0, test_data.Length);
}
timer.Stop();
long test = timer.ElapsedMilliseconds;
//Write time elapsed and file size to CSV file
流和编码以配置文件创建JSON请求
var timer = new Stopwatch();
timer.Start();
using (FileStream fs = File.Open(file_path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] file_bytes = new byte[fs.Length];
fs.Read(file_bytes, 0, file_bytes.Length);
ret_file = Convert.ToBase64String(file_bytes);
}
timer.Stop();
long test = timer.ElapsedMilliseconds;
//Write time elapsed, file size, and length of UTF8 encoded ret_file string to CSV file
结果是,简单读取始终花费0ms,而Base64编码最多花费5ms。以下是最长的时间:
File Size | Output Stream Size | Time
1352KB 1802KB 5ms
1031KB 1374KB 7ms
463KB 617KB 1ms
但是,在生产中,如果没有先检查定界符就不会盲目地编写multipart / form-data吗?因此,我修改了表单数据代码,以便检查文件本身中的定界符字节,以确保一切正常。我没有编写优化的扫描算法,所以我只是将定界符缩小了,这样就不会浪费很多时间。
var timer = new Stopwatch();
timer.Start();
using (FileStream fs = File.Open(file_path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] test_data = new byte[fs.Length];
fs.Read(test_data, 0, test_data.Length);
string delim = "--DXX";
byte[] delim_checker = Encoding.UTF8.GetBytes(delim);
for (int i = 0; i <= test_data.Length - delim_checker.Length; i++)
{
bool match = true;
for (int j = i; j < i + delim_checker.Length; j++)
{
if (test_data[j] != delim_checker[j - i])
{
match = false;
break;
}
}
if (match)
{
break;
}
}
}
timer.Stop();
long test = timer.ElapsedMilliseconds;
现在的结果向我显示,表单数据方法实际上将大大降低速度。以下是两种方法中时间> 0ms的结果:
File Size | FormData Time | Json/Base64 Time
181Kb 1ms 0ms
1352Kb 13ms 4ms
463Kb 4ms 5ms
133Kb 1ms 0ms
133Kb 1ms 0ms
129Kb 1ms 0ms
284Kb 2ms 1ms
1031Kb 9ms 3ms
看来我的定界符只有5个字符长,优化的算法似乎也不会做得更好。无论如何都不是3倍更好,这是执行Base64编码而不是检查文件字节是否有定界符的性能优势。
显然,Base64编码将使大小膨胀,如我在第一张表中所示,但是即使使用支持Unicode的UTF-8,它的大小也没有那么糟,并且在需要时可以很好地压缩。但是真正的好处是我的代码很漂亮,干净而且易于理解,并且对JSON请求有效载荷的关注也没有太大的麻烦。
那么,为什么不是所有人都会简单地将Base64编码为JSON文件而不使用multipart / form-data呢?有标准,但是这些标准确实会经常更改。无论如何,标准真的只是建议吗?