c# 模拟表单提交,post form 上传文件、数据内容
转自:https://www.cnblogs.com/DoNetCShap/p/10696277.html
表单提交协议规定:
要先将 HTTP 要求的 Content-Type 设为 multipart/form-data,而且要设定一个 boundary 参数,
这个参数是由应用程序自行产生,它会用来识别每一份资料的边界 (boundary),
用以产生多重信息部份 (message part)。
而 HTTP 服务器可以抓取 HTTP POST 的信息,
基本内容:
1. 每个信息部份都要用 --[BOUNDARY_NAME] 来包装,以分隔出信息的每个部份,而最后要再加上一个 --[BOUNDARY_NAME] 来表示结束。
2. 每个信息部份都要有一个 Content-Disposition: form-data; name="",而 name 设定的就是 HTTP POST 的键值 (key)。
3. 声明区和值区中间要隔两个新行符号(\r\n)。
4. 中间可以夹入二进制资料,但二进制资料必须要格式化为二进制字符串。
5. 若要设定不同信息段的资料型别 (Content-Type),则要在信息段内的声明区设定。
两个form内容模板
boundary = "----" + DateTime.Now.Ticks.ToString("x");//程序生成
1.文本内容
"\r\n--" + boundary +
"\r\nContent-Disposition: form-data; name=\"键\"; filename=\"文件名\"" +
"\r\nContent-Type: application/octet-stream" +
"\r\n\r\n";
2.文件内容
"\r\n--" + boundary +
"\r\nContent-Disposition: form-data; name=\"键\"" +
"\r\n\r\n内容";
1 /// <summary> 2 /// 表单数据项 3 /// </summary> 4 public class FormItemModel 5 { 6 /// <summary> 7 /// 表单键,request["key"] 8 /// </summary> 9 public string Key { set; get; } 10 /// <summary> 11 /// 表单值,上传文件时忽略,request["key"].value 12 /// </summary> 13 public string Value { set; get; } 14 /// <summary> 15 /// 是否是文件 16 /// </summary> 17 public bool IsFile 18 { 19 get 20 { 21 if (FileContent==null || FileContent.Length == 0) 22 return false; 23 24 if (FileContent != null && FileContent.Length > 0 && string.IsNullOrWhiteSpace(FileName)) 25 throw new Exception("上传文件时 FileName 属性值不能为空"); 26 return true; 27 } 28 } 29 /// <summary> 30 /// 上传的文件名 31 /// </summary> 32 public string FileName { set; get; } 33 /// <summary> 34 /// 上传的文件内容 35 /// </summary> 36 public Stream FileContent { set; get; } 37 }
1 /// <summary> 2 /// 使用Post方法获取字符串结果 3 /// </summary> 4 /// <param name="url"></param> 5 /// <param name="formItems">Post表单内容</param> 6 /// <param name="cookieContainer"></param> 7 /// <param name="timeOut">默认20秒</param> 8 /// <param name="encoding">响应内容的编码类型(默认utf-8)</param> 9 /// <returns></returns> 10 public static string PostForm(string url, List<FormItemModel> formItems, CookieContainer cookieContainer = null, string refererUrl = null, Encoding encoding = null,int timeOut = 20000) 11 { 12 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); 13 #region 初始化请求对象 14 request.Method = "POST"; 15 request.Timeout = timeOut; 16 request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"; 17 request.KeepAlive = true; 18 request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36"; 19 if (!string.IsNullOrEmpty(refererUrl)) 20 request.Referer = refererUrl; 21 if (cookieContainer != null) 22 request.CookieContainer = cookieContainer; 23 #endregion 24 25 string boundary = "----" + DateTime.Now.Ticks.ToString("x");//分隔符 26 request.ContentType = string.Format("multipart/form-data; boundary={0}", boundary); 27 //请求流 28 var postStream = new MemoryStream(); 29 #region 处理Form表单请求内容 30 //是否用Form上传文件 31 var formUploadFile = formItems != null && formItems.Count > 0; 32 if (formUploadFile) 33 { 34 //文件数据模板 35 string fileFormdataTemplate = 36 "\r\n--" + boundary + 37 "\r\nContent-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + 38 "\r\nContent-Type: application/octet-stream" + 39 "\r\n\r\n"; 40 //文本数据模板 41 string dataFormdataTemplate = 42 "\r\n--" + boundary + 43 "\r\nContent-Disposition: form-data; name=\"{0}\"" + 44 "\r\n\r\n{1}"; 45 foreach (var item in formItems) 46 { 47 string formdata = null; 48 if (item.IsFile) 49 { 50 //上传文件 51 formdata = string.Format( 52 fileFormdataTemplate, 53 item.Key, //表单键 54 item.FileName); 55 } 56 else 57 { 58 //上传文本 59 formdata = string.Format( 60 dataFormdataTemplate, 61 item.Key, 62 item.Value); 63 } 64 65 //统一处理 66 byte[] formdataBytes = null; 67 //第一行不需要换行 68 if (postStream.Length == 0) 69 formdataBytes = Encoding.UTF8.GetBytes(formdata.Substring(2, formdata.Length - 2)); 70 else 71 formdataBytes = Encoding.UTF8.GetBytes(formdata); 72 postStream.Write(formdataBytes, 0, formdataBytes.Length); 73 74 //写入文件内容 75 if (item.FileContent != null && item.FileContent.Length>0) 76 { 77 using (var stream = item.FileContent) 78 { 79 byte[] buffer = new byte[1024]; 80 int bytesRead = 0; 81 while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0) 82 { 83 postStream.Write(buffer, 0, bytesRead); 84 } 85 } 86 } 87 } 88 //结尾 89 var footer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n"); 90 postStream.Write(footer, 0, footer.Length); 91 92 } 93 else 94 { 95 request.ContentType = "application/x-www-form-urlencoded"; 96 } 97 #endregion 98 99 request.ContentLength = postStream.Length; 100 101 #region 输入二进制流 102 if (postStream != null) 103 { 104 postStream.Position = 0; 105 //直接写入流 106 Stream requestStream = request.GetRequestStream(); 107 108 byte[] buffer = new byte[1024]; 109 int bytesRead = 0; 110 while ((bytesRead = postStream.Read(buffer, 0, buffer.Length)) != 0) 111 { 112 requestStream.Write(buffer, 0, bytesRead); 113 } 114 115 ////debug 116 //postStream.Seek(0, SeekOrigin.Begin); 117 //StreamReader sr = new StreamReader(postStream); 118 //var postStr = sr.ReadToEnd(); 119 postStream.Close();//关闭文件访问 120 } 121 #endregion 122 123 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 124 if (cookieContainer != null) 125 { 126 response.Cookies = cookieContainer.GetCookies(response.ResponseUri); 127 } 128 129 using (Stream responseStream = response.GetResponseStream()) 130 { 131 using (StreamReader myStreamReader = new StreamReader(responseStream, encoding ?? Encoding.UTF8)) 132 { 133 string retString = myStreamReader.ReadToEnd(); 134 return retString; 135 } 136 } 137 }
运行方式:
1 var url = "http://127.0.0.1/testformdata.aspx?aa=1&bb=2&ccc=3"; 2 var log1=@"D:\temp\log1.txt"; 3 var log2 = @"D:\temp\log2.txt"; 4 var formDatas = new List<Grass.Net.FormItemModel>(); 5 //添加文件 6 formDatas.Add(new Grass.Net.FormItemModel() 7 { 8 Key="log1", 9 Value="", 10 FileName = "log1.txt", 11 FileContent=File.OpenRead(log1) 12 }); 13 formDatas.Add(new Grass.Net.FormItemModel() 14 { 15 Key = "log2", 16 Value = "", 17 FileName = "log2.txt", 18 FileContent = File.OpenRead(log2) 19 }); 20 //添加文本 21 formDatas.Add(new Grass.Net.FormItemModel() 22 { 23 Key = "id", 24 Value = "id-test-id-test-id-test-id-test-id-test-" 25 }); 26 formDatas.Add(new Grass.Net.FormItemModel() 27 { 28 Key = "name", 29 Value = "name-test-name-test-name-test-name-test-name-test-" 30 }); 31 //提交表单 32 var result = PostForm(url, formDatas);
以下内容做了一些优化修改:
1 if (formUploadFile) 2 { 3 //文件数据模板 4 string fileFormdataTemplate = 5 "\r\n--" + boundary + 6 "\r\nContent-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + 7 "\r\nContent-Type: application/octet-stream" + 8 "\r\n\r\n"; 9 //文本数据模板 10 string dataFormdataTemplate = 11 "\r\n--" + boundary + 12 "\r\nContent-Disposition: form-data; name=\"{0}\"" + 13 "\r\n\r\n{1}"; 14 //头部 15 var header = Encoding.UTF8.GetBytes("--" + boundary); 16 postStream.Write(header, 0, header.Length); 17 var index = 0; 18 foreach (var item in formItems) 19 { 20 index++; 21 string formdata = null; 22 if (item.IsFile) 23 { 24 if (index == 1) 25 { 26 formdata = string.Format("\r\nContent-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n", item.Key, item.FileName); 27 28 } 29 else 30 { 31 //上传文件 32 formdata = string.Format(fileFormdataTemplate,item.Key,item.FileName); 33 34 } 35 36 } 37 else 38 { 39 if (index==1) 40 { 41 formdata = string.Format("\r\nContent-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}", item.Key, item.FileName); 42 } 43 else 44 { 45 //上传文本 46 formdata = string.Format(dataFormdataTemplate, item.Key, item.Value); 47 } 48 49 } 50 51 //统一处理 52 byte[] formdataBytes = Encoding.UTF8.GetBytes(formdata); 53 54 postStream.Write(formdataBytes, 0, formdataBytes.Length); 55 56 //写入文件内容 57 //if (item.FileContent != null && item.FileContent.Length > 0) 58 if (item.IsFile) 59 { 60 61 FileStream fs = new FileStream(item.FilePath, FileMode.Open, FileAccess.Read);//可以是其他重载方法 62 byte[] byData = new byte[fs.Length]; 63 fs.Read(byData, 0, byData.Length); 64 fs.Close(); 65 postStream.Write(byData, 0, byData.Length); 66 } 67 } 68 //结尾 69 var footer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--"); 70 postStream.Write(footer, 0, footer.Length); 71 72 }