在很多企业级应用中,我们都没法直接通过开发语言sdk包封装的http工具来模拟http复合表单(multipart/form-data),特别是在跨语言跨平台的编程过程中,其实实现方案并不复杂,只要你了解了http协议中复合表单的报文结构就很简单了:
httpheader
------时间戳------
表单参数1
------时间戳------
表单参数2
------时间戳------
文件1的描述+二进制信息
------时间戳------
文件2的描述+二进制信息
下面我们进一步以一段c#的代实例码来演示下这个结构:
///<summary>
///向服务器发送混合型的请求,1:成功发送,0:发送失败
///</summary>
///<param name="paranames">表单参数名数组</param>
///<param name="paravalues">参数值数组</param>
///<param name="files">文件名数组</param>
///<param name="errmsg">报错信息</param>
///<returns></returns>
public int SendRequest(string[] paranames, string[] paravalues, string[] files, ref string errmsg)
{
StringBuilder http, text;
byte[] httpbyte;
byte[] textbyte = null;
long length = 0;
DateTime now = DateTime.Now;
List<byte[]> data =new List<byte[]>();
//构造时间戳
string strBoundary = "------------" + DateTime.Now.Ticks.ToString("x");
byte[] boundary = Encoding.ASCII.GetBytes("\r\n" + strBoundary +"\r\n");
length += boundary.Length;
//构造时间戳
//加载表单参数信息
if (paranames != null)
{
text = new StringBuilder();
for (int i = 0; i < paranames.Length; i++)
{
text.Append("--");
text.Append(strBoundary);//添加时间戳
text.Append("\r\n");
text.Append("Content-Disposition: form-data; name=\"" + paranames[i] +"\"\r\n\r\n");
text.Append(paravalues[i]);
text.Append("\r\n");
}
string para = text.ToString();
textbyte = Encoding.ASCII.GetBytes(para);
length += textbyte.Length;
}
//加载文件信息
if (files != null)
{
for (int i = 0; i < files.Length; i++)
{
FileStream fs;
StringBuilder sbfile =new StringBuilder();
try
{
fs = File.Open(files[i],FileMode.Open, FileAccess.Read,FileShare.Read);
if (i == 0) sbfile.Append("--");//添加文件
else sbfile.Append("\r\n--");
sbfile.Append(strBoundary);//添加时间戳
sbfile.Append("\r\n");
sbfile.Append("Content-Disposition: form-data; name=\"");
sbfile.Append("file");
sbfile.Append("\"; filename=\"");
sbfile.Append(Path.GetFileName(files[i]));
sbfile.Append("\"");
sbfile.Append("\r\n");
sbfile.Append("Content-Type: ");
sbfile.Append("application/octet-stream");
sbfile.Append("\r\nContent-Length:");
sbfile.Append(fs.Length.ToString());
sbfile.Append("\r\n");
sbfile.Append("\r\n");
string temp = sbfile.ToString();
byte[] bin =Encoding.UTF8.GetBytes(temp);
data.Add(bin);
length += bin.Length;
length += fs.Length;
fs.Close();
}
catch (Exception exc)
{
errmsg = exc.Message.ToString();
return 0;
}
}
}
//构造http头
http = new StringBuilder();
http.Append("POST " + ur.ToString() +" HTTP/1.1\r\n");
http.Append("Content-Type:multipart/form-data;boundary=");
http.Append(strBoundary);
http.Append("\r\n");
http.Append("Host:" + ipaddress +":" + tcpport.ToString() + "\r\n");
http.Append("Content-Length:");
http.Append(length.ToString());
http.Append("\r\n");
http.Append("Expect: 100-continue\r\n");//注明要在收到服务器的continue消息后才继续上传http消息体
http.Append("Connection: Keep-Alive\r\n\r\n");
string strtemp = http.ToString();
httpbyte = Encoding.ASCII.GetBytes(strtemp);
try
{
soc.Send(httpbyte);"//首先发送http头
Thread.Sleep(100);
string check = GetResponse();
if (check == null || !check.Contains("Continue"))//得到服务器确认后才继续上传
{
errmsg = "客户端已成功发送请求,但服务器没有响应!";
return 0;
}
if (paranames != null)
{
soc.Send(textbyte, textbyte.Length, SocketFlags.None);//发送表单参数
}
if (files != null)
{//依次发送文件
for (int i = 0; i < data.Count; i++)
{
int size = 0;
FileStream fs =File.Open(files[i], FileMode.Open, FileAccess.Read, FileShare.Read);
soc.Send(data[i], data[i].Length, SocketFlags.None);
byte[] buff =new byte[1024];
size = fs.Read(buff, 0, 1024);
while (size > 0)
{
soc.Send(buff, size, SocketFlags.None);
size = fs.Read(buff, 0, 1024);
}
fs.Close();
}
}
soc.Send(boundary, boundary.Length, SocketFlags.None);
return 1;
}
catch (Exception exc)
{
errmsg = exc.Message.ToString();
return 0;
}
}