Multipart Form Post in C#
2015-11-16 18:01
465 查看
I recently had to access a web API through C Sharp that required a file upload. This is pretty easy if you have an HTML page with a form tag and you want a user to directly upload the file.
However, this is not always a reasonable path to take. Sometimes you may be wanting to access a file that is already in a system and you don’t want a new upload. If you are accessing an external API, this is probably always the case. Unfortunately, building
this post using C# is not quite as straightforward. I first tried using the WebClient UploadFile method, but it didn’t fit my needs because I wanted to upload form values (id, filename, other API specific parameters) in addition to just a file.
So, I needed to roll my own form post. Here is the Multipart Form RFC and the W3C Specification for
multipart/form data. After reading these links and searching some forums, here is what I came up with.
Update: This post has gotten a great response from all the readers who have taken the time to comment and contribute. I would like to take this opportunity to promotethe best REST Client for
.NET, RestSharp. John Sheehan has implemented this technique using code from this post, which can be seen on github (just look for WriteMultipartFormData).
He has also done a great job implementing other basic REST operations in a fully tested suite. I would recommend reading the rest of the post to figure out what is going on behind the scenes, but you might consider using RestSharp in a production environment.
Thanks for reading!
Note: If anyone is interested in this code in Visual Basic, reader Mike Ferreira converted the code into VB.Net in a comment below.
Here is the code to call the MultipartFormDataPost function with multiple parameters, including a file.
<form method="POST" action="http://localhost/" enctype="multipart/form-data"> File : <input type="file" name="content" size="38" /><br /> <input type="hidden" name="id" value='fileUpload' /> </form> |
this post using C# is not quite as straightforward. I first tried using the WebClient UploadFile method, but it didn’t fit my needs because I wanted to upload form values (id, filename, other API specific parameters) in addition to just a file.
So, I needed to roll my own form post. Here is the Multipart Form RFC and the W3C Specification for
multipart/form data. After reading these links and searching some forums, here is what I came up with.
Update: This post has gotten a great response from all the readers who have taken the time to comment and contribute. I would like to take this opportunity to promotethe best REST Client for
.NET, RestSharp. John Sheehan has implemented this technique using code from this post, which can be seen on github (just look for WriteMultipartFormData).
He has also done a great job implementing other basic REST operations in a fully tested suite. I would recommend reading the rest of the post to figure out what is going on behind the scenes, but you might consider using RestSharp in a production environment.
Thanks for reading!
Note: If anyone is interested in this code in Visual Basic, reader Mike Ferreira converted the code into VB.Net in a comment below.
// Implements multipart/form-data POST in C# http://www.ietf.org/rfc/rfc2388.txt // http://www.briangrinstead.com/blog/multipart-form-post-in-c public static class FormUpload { private static readonly Encoding encoding = Encoding.UTF8; public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters) { string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid()); string contentType = "multipart/form-data; boundary=" + formDataBoundary; byte[] formData = GetMultipartFormData(postParameters, formDataBoundary); return PostForm(postUrl, userAgent, contentType, formData); } private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData) { HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest; if (request == null) { throw new NullReferenceException("request is not a http request"); } // Set up the request properties. request.Method = "POST"; request.ContentType = contentType; request.UserAgent = userAgent; request.CookieContainer = new CookieContainer(); request.ContentLength = formData.Length; // You could add authentication here as well if needed: // request.PreAuthenticate = true; // request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested; // request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes("username" + ":" + "password"))); // Send the form data to the request. using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(formData, 0, formData.Length); requestStream.Close(); } return request.GetResponse() as HttpWebResponse; } private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary) { Stream formDataStream = new System.IO.MemoryStream(); bool needsCLRF = false; foreach (var param in postParameters) { // Thanks to feedback from commenters, add a CRLF to allow multiple parameters to be added. // Skip it on the first parameter, add it to subsequent parameters. if (needsCLRF) formDataStream.Write(encoding.GetBytes("\r\n"), 0, encoding.GetByteCount("\r\n")); needsCLRF = true; if (param.Value is FileParameter) { FileParameter fileToUpload = (FileParameter)param.Value; // Add just the first part of this param, since we will write the file data directly to the Stream string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n", boundary, param.Key, fileToUpload.FileName ?? param.Key, fileToUpload.ContentType ?? "application/octet-stream"); formDataStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header)); // Write the file data directly to the Stream, rather than serializing it to a string. formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length); } else { string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}", boundary, param.Key, param.Value); formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData)); } } // Add the end of the request. Start with a newline string footer = "\r\n--" + boundary + "--\r\n"; formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer)); // Dump the Stream into a byte[] formDataStream.Position = 0; byte[] formData = new byte[formDataStream.Length]; formDataStream.Read(formData, 0, formData.Length); formDataStream.Close(); return formData; } public class FileParameter { public byte[] File { get; set; } public string FileName { get; set; } public string ContentType { get; set; } public FileParameter(byte[] file) : this(file, null) { } public FileParameter(byte[] file, string filename) : this(file, filename, null) { } public FileParameter(byte[] file, string filename, string contenttype) { File = file; FileName = filename; ContentType = contenttype; } } } |
// Read file data FileStream fs = new FileStream("c:\\people.doc", FileMode.Open, FileAccess.Read); byte[] data = new byte[fs.Length]; fs.Read(data, 0, data.Length); fs.Close(); // Generate post objects Dictionary<string, object> postParameters = new Dictionary<string, object>(); postParameters.Add("filename", "People.doc"); postParameters.Add("fileformat", "doc"); postParameters.Add("file", new FormUpload.FileParameter(data, "People.doc", "application/msword")); // Create request and receive response string postURL = "http://localhost"; string userAgent = "Someone"; HttpWebResponse webResponse = FormUpload.MultipartFormDataPost(postURL, userAgent, postParameters); // Process response StreamReader responseReader = new StreamReader(webResponse.GetResponseStream()); string fullResponse = responseReader.ReadToEnd(); webResponse.Close(); Response.Write(fullResponse);
相关文章推荐
- C# 格式化表
- (原创)c#学习笔记10--定义类成员06--示例应用程序03--类库的客户应用程序
- C# 文件从utf8或系统默认格式转gbk写入新文件(支持有bom和无bom)
- (原创)c#学习笔记10--定义类成员06--示例应用程序02--编写类库
- c#中异步调用asyn
- (原创)c#学习笔记10--定义类成员06--示例应用程序01--规划应用程序
- C#判断手机访问和电脑访问程序代码
- (原创)c#学习笔记10--定义类成员05--部分方法定义
- C#位运算讲解与示例[转]
- (原创)c#学习笔记10--定义类成员04--部分类定义
- C#委托的介绍(delegate、Action、Func、predicate)
- C#稳固基础:传统遍历与迭代器
- C#集合之STACK
- C# 计算时间差 用timespan函数
- C# 获取当前文件名、执行函数、执行代码行数和列数
- C# Stopwatch
- C# 中? 和 ?? 在变量中的使用
- c# delegate的invoke和bejinInvoke的区别
- C#编程实现DataTable添加行的方法
- C#基于委托实现多线程之间操作的方法