HTTP的POST请求的详细构造[实现机器提交表单含上传文件及普通文本域]
最近用C#做测试网站的服务程序,由于有些接口需要真实的测试数据。比如提交表单中的文本域和文件域,但.Net中只提供了简单的单独提交文本或文件的方法,所以只能自己构造格式为
multipart/form-data
的HTTP请求,而让我更加了解了HTTP请求的格式。
下面是在Google搜索后的一个解决方案,原链接:http://stackoverflow.com/questions/566462/upload-files-with-httpwebrequest-multipart-form-data
方法1:上传文本域及一个文件[测试成功]
1 //发送对应表单文本域和一个文件[函数中的输出语句自己调节] 2 public static void HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc) 3 { 4 Console.WriteLine(string.Format("Uploading {0} to {1}", file, url)); 5 string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x"); 6 byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n"); 7 8 HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url); 9 wr.ContentType = "multipart/form-data; boundary=" + boundary; 10 wr.Method = "POST"; 11 wr.KeepAlive = true; 12 wr.Credentials = System.Net.CredentialCache.DefaultCredentials; 13 14 Stream rs = wr.GetRequestStream(); 15 16 string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; 17 foreach (string key in nvc.Keys) 18 { 19 rs.Write(boundarybytes, 0, boundarybytes.Length); 20 string formitem = string.Format(formdataTemplate, key, nvc[key]); 21 byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem); 22 rs.Write(formitembytes, 0, formitembytes.Length); 23 } 24 rs.Write(boundarybytes, 0, boundarybytes.Length); 25 26 string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n"; 27 string header = string.Format(headerTemplate, paramName, file, contentType); 28 byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header); 29 rs.Write(headerbytes, 0, headerbytes.Length); 30 31 FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read); 32 byte[] buffer = new byte[4096]; 33 int bytesRead = 0; 34 while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) 35 { 36 rs.Write(buffer, 0, bytesRead); 37 } 38 fileStream.Close(); 39 40 byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n"); 41 rs.Write(trailer, 0, trailer.Length); 42 rs.Close(); 43 44 WebResponse wresp = null; 45 try 46 { 47 wresp = wr.GetResponse(); 48 Stream stream2 = wresp.GetResponseStream(); 49 StreamReader reader2 = new StreamReader(stream2); 50 Console.WriteLine(string.Format("File uploaded, server response is: {0}", reader2.ReadToEnd())); 51 } 52 catch (Exception ex) 53 { 54 //log.Error("Error uploading file", ex); 55 if (wresp != null) 56 { 57 wresp.Close(); 58 wresp = null; 59 } 60 } 61 finally 62 { 63 wr = null; 64 } 65 }
//使用的例子 //nvc中存放表单文本域的键值对 //image/jpeg是上传文件的格式 NameValueCollection nvc = new NameValueCollection(); nvc.Add("id", "TTR"); nvc.Add("btn-submit-photo", "Upload"); HttpUploadFile("http://your.server.com/upload", @"C:\test\test.jpg", "file", "image/jpeg", nvc);
方法2:上传表单文本域和多个文件[我没测试成功]
1 public static void UploadFilesToRemoteUrl(string url, string[] files, string 2 logpath, NameValueCollection nvc) 3 { 4 5 long length = 0; 6 string boundary = "----------------------------" + 7 DateTime.Now.Ticks.ToString("x"); 8 9 10 HttpWebRequest httpWebRequest2 = (HttpWebRequest)WebRequest.Create(url); 11 httpWebRequest2.ContentType = "multipart/form-data; boundary=" + 12 boundary; 13 httpWebRequest2.Method = "POST"; 14 httpWebRequest2.KeepAlive = true; 15 httpWebRequest2.Credentials = 16 System.Net.CredentialCache.DefaultCredentials; 17 18 19 20 Stream memStream = new System.IO.MemoryStream(); 21 22 byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + 23 boundary + "\r\n"); 24 25 26 string formdataTemplate = "\r\n--" + boundary + 27 "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}"; 28 29 foreach (string key in nvc.Keys) 30 { 31 string formitem = string.Format(formdataTemplate, key, nvc[key]); 32 byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem); 33 memStream.Write(formitembytes, 0, formitembytes.Length); 34 } 35 36 37 memStream.Write(boundarybytes, 0, boundarybytes.Length); 38 39 string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n Content-Type: application/octet-stream\r\n\r\n"; 40 41 for (int i = 0; i < files.Length; i++) 42 { 43 44 //string header = string.Format(headerTemplate, "file" + i, files[i]); 45 string header = string.Format(headerTemplate, "uplTheFile", files[i]); 46 47 byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header); 48 49 memStream.Write(headerbytes, 0, headerbytes.Length); 50 51 52 FileStream fileStream = new FileStream(files[i], FileMode.Open, 53 FileAccess.Read); 54 byte[] buffer = new byte[1024]; 55 56 int bytesRead = 0; 57 58 while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) 59 { 60 memStream.Write(buffer, 0, bytesRead); 61 62 } 63 64 65 memStream.Write(boundarybytes, 0, boundarybytes.Length); 66 67 68 fileStream.Close(); 69 } 70 71 httpWebRequest2.ContentLength = memStream.Length; 72 73 Stream requestStream = httpWebRequest2.GetRequestStream(); 74 75 memStream.Position = 0; 76 byte[] tempBuffer = new byte[memStream.Length]; 77 memStream.Read(tempBuffer, 0, tempBuffer.Length); 78 memStream.Close(); 79 requestStream.Write(tempBuffer, 0, tempBuffer.Length); 80 requestStream.Close(); 81 82 83 WebResponse webResponse2 = httpWebRequest2.GetResponse(); 84 85 Stream stream2 = webResponse2.GetResponseStream(); 86 StreamReader reader2 = new StreamReader(stream2); 87 88 89 MessageBox.Show(reader2.ReadToEnd()); 90 91 webResponse2.Close(); 92 httpWebRequest2 = null; 93 webResponse2 = null; 94 }
原理解释:
详细见链接。下面简单复制过来的。
I believe that you are not building the request body correctly. First, you need to include part boundary (random text) in content type header. For example,
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarySkAQdHysJKel8YBM
Now format of request body will be something like
------WebKitFormBoundarySkAQdHysJKel8YBM
Content-Disposition: form-data;name="key"
KeyValueGoesHere
------WebKitFormBoundarySkAQdHysJKel8YBM
Content-Disposition: form-data;name="param2"
ValueHere
------WebKitFormBoundarySkAQdHysJKel8YBM
Content-Disposition: form-data;name="fileUpload"; filename="y1.jpg"
Content-Type: image/jpeg
[image data goes here]
I will suggest you to use tool such as Fiddler to understand how these requests are built.