[Basically the same as this guy, unfortunately the thread died before being answered.]WebClient's UploadFile and UploadData not sending same headers (with multipart/form-data)
I can upload a file using the WebClients UploadFile, but I'm unable to upload the same exact file converted to a byte array using UploadData.
Fiddler difference:
On UploadData: Content-Length: 13264
Content-Type: multipart/form-data; boundary=---------------------8d77fde3018f8e5
On UploadFile: Content-Length: 13457
Content-Type: multipart/form-data; boundary=---------------------8d77fdcfcc453c5
Why is this happening? Currently the file i have is stored in memory and not on file, and saving the file locally isn't really an option.
Anyone has any ideas?
EDIT:
var bytes = System.IO.File.ReadAllBytes("dummy.pdf");
using (WebClient client = new WebClient())
{
client.Headers.Add("Access-Token", token);
client.Headers.Add("Client-Secret", clientSecret);
client.UploadFile("website", "dummy.pdf");
}
string boundary = "---------------------" + DateTime.Now.Ticks.ToString("x");
var newLine = Environment.NewLine;
var fileHeaderFormat = "--"+boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + newLine +
"Content-Type: application/octet-stream" + newLine + newLine;
var req = (HttpWebRequest)HttpWebRequest.Create(#"website");
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data; boundary=" + boundary;
req.Headers.Add("Access-Token", token);
req.Headers.Add("Client-Secret", clientSecret);
req.Headers.GetType().InvokeMember("ChangeInternal",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
Type.DefaultBinder, req.Headers, new object[] { "Connection", "Keep-Alive" }
);
using (var reqStream = req.GetRequestStream())
{
var reqWriter = new StreamWriter(reqStream);
var tmp = string.Format(fileHeaderFormat, "file", "dummy.pdf");
reqWriter.Write(tmp);
reqWriter.Flush();
reqStream.Write(bytes, 0, bytes.Length);
}
var res = req.GetResponse();
using (var resStream = res.GetResponseStream())
{
var reader = new StreamReader(resStream);
var t = reader.ReadToEnd();
}
Related
I was trying to upload a file to a web server through UWP c# using filestream but it always gives me an error i.e 405 method not allowed when I try to upload on http://example.com/httpdocs/content. Even for testing purpose I tried uploading on my localhost but still no luck.
Any Help?
Code :
public async Task<bool> Upload(StorageFile fileName)
{
HttpMultipartFormDataContent form = new HttpMultipartFormDataContent();
cts = new CancellationTokenSource();
using (IInputStream fileStream = await fileName.OpenSequentialReadAsync())
{
HttpStreamContent content = new HttpStreamContent(fileStream);
form.Add(content, "premier", fileName.Name);
using (HttpClient client = new HttpClient())
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, new Uri("http://example.com/httpdocs/content")))
{
request.Content = form;
request.Headers.TryAppendWithoutValidation("Content-Type", "application/x-www-form-urlencoded");
HttpResponseMessage response = await client.SendRequestAsync(request).AsTask(cts.Token);
var result = response.Content.ReadAsStringAsync().GetResults();
}
}
}
return true;
}
Hye, after trying much I found this code and it works perfectly. With a little correction that the request will be now sent to an aspx page i.e. http://example.com/abc.aspx. If you wanna send high mb's of excel file data then just change 4096 in Math.Min(4096, (int)fileStream.Length) accordingly
Client Side Code-
public static async Task<string> UploadFileEx(string uploadfile, string
url, string fileFormName, string contenttype, NameValueCollection
querystring, CookieContainer cookies)
{
try
{
if ((fileFormName == null) ||
(fileFormName.Length == 0))
{
fileFormName = "file";
}
if ((contenttype == null) ||
(contenttype.Length == 0))
{
contenttype = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
}
string postdata;
postdata = "?";
if (querystring != null)
{
foreach (string key in querystring.Keys)
{
postdata += key + "=" + querystring.Get(key) + "&";
}
}
Uri uri = new Uri(url + postdata);
string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(uri);
webrequest.CookieContainer = cookies;
webrequest.ContentType = "multipart/form-data; boundary=" + boundary;
webrequest.Method = "POST";
// Build up the post message header
StringBuilder sb = new StringBuilder();
sb.Append("--");
sb.Append(boundary);
sb.Append("\r\n");
sb.Append("Content-Disposition: form-data; name=\"");
sb.Append(fileFormName);
sb.Append("\"; filename=\"");
var sd = sb.Append(Path.GetFileName(uploadfile));
sb.Append("\"");
sb.Append("\r\n");
sb.Append("Content-Type: ");
sb.Append(contenttype);
sb.Append("\r\n");
sb.Append("\r\n");
string postHeader = sb.ToString();
byte[] postHeaderBytes = Encoding.UTF8.GetBytes(postHeader);
// Build the trailing boundary string as a byte array
// ensuring the boundary appears on a line by itself
byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
FileStream fileStream = new FileStream(uploadfile, FileMode.Open, FileAccess.Read);
long length = postHeaderBytes.Length + fileStream.Length + boundaryBytes.Length;
// webrequest.ContentLength = length;
webrequest.Headers[HttpRequestHeader.ContentLength] = length.ToString();
//Stream requestStream = webrequest.GetRequestStream();
Stream requestStream = await webrequest.GetRequestStreamAsync();
// Write out our post header
requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);
// Write out the file contents
byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
requestStream.Write(buffer, 0, bytesRead);
// Write out the trailing boundary
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
WebResponse response = await webrequest.GetResponseAsync();
Stream s = response.GetResponseStream();
StreamReader sr = new StreamReader(s);
return sr.ReadToEnd();
}
catch (Exception e)
{
e.ToString();
}
return null;
}
I want to send a message and photo to a channel with my bot in c# 2013.
Message and photo should be sent in one box. Photo in above the message .
i can send a message to the channel successfully but there are 2 problems:
when send photo , this error shows:
The remote server returned an error: (400) Bad Request.
I cannot send a text and photo together in only 1 send.
code :
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Telegram.Bot;
using Telegram.Bot.Types;
using System.Net;
namespace SendTxt-Photo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string Token = "adasdsadsadsadasds";
string channel_id = "#BestLaptop";
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
using (var stream = File.Open(#"image/1.jpg", FileMode.Open))
{
WebRequest req = WebRequest.Create("https://api.telegram.org/bot" + Token + "/sendMessage?chat_id=" + channel_id + "&text=" + textbox1.text);
req.UseDefaultCredentials = true;
WebRequest req1 = WebRequest.Create("https://api.telegram.org/bot" + Token + "/sendPhoto?chat_id=" + channel_id + "&Photo=" + stream );
req.UseDefaultCredentials = true;
req1.UseDefaultCredentials = true;
var result = req.GetResponse();
req.Abort();
var result1 = req1.GetResponse();
req1.Abort();
}
}
}
}
At first you must know that you send ...&photo=Sistem.IO.FileStream instead your file. It's root cause of bad request. Take a look in debug.
At second as you can see in api documentation sendPhoto method provide three types of photo parameter:
Pass a file_id as String to send a photo that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a photo from the Internet, or upload a new photo using multipart/form-data.
So using multipart/form-data required in your case. It can be simplified by using RestSharp for example like (code was generated by Postman, haven't tested):
var client = new RestClient("https://api.telegram.org/botadasdsadsadsadasds/sendPhoto");
var request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
request.AddParameter("multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW", "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"chat_id\"\r\n\r\n#BestLaptop\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"1.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
At third you can't sent photo above text in one message. For sending text and photo in one message use caption parameter of sendPhoto method. It provide 200 symbols length.
PS Why not using the Telegram.Bot nuget package?
This function is helper to upload file:
public static string UploadFilesToRemoteUrl(HttpWebRequest request, string[] files, NameValueCollection formFields = null)
{
string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.Method = "POST";
request.KeepAlive = true;
Stream memStream = new System.IO.MemoryStream();
var boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" +
boundary + "\r\n");
var endBoundaryBytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" +
boundary + "--");
string formdataTemplate = "\r\n--" + boundary +
"\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";
if (formFields != null)
{
foreach (string key in formFields.Keys)
{
string formitem = string.Format(formdataTemplate, key, formFields[key]);
byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
memStream.Write(formitembytes, 0, formitembytes.Length);
}
}
string headerTemplate =
"Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" +
"Content-Type: application/octet-stream\r\n\r\n";
for (int i = 0; i < files.Length; i++)
{
memStream.Write(boundarybytes, 0, boundarybytes.Length);
var header = string.Format(headerTemplate, "photo", files[i]);
var headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
memStream.Write(headerbytes, 0, headerbytes.Length);
using (var fileStream = new FileStream(files[i], FileMode.Open, FileAccess.Read))
{
var buffer = new byte[1024];
var bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
memStream.Write(buffer, 0, bytesRead);
}
}
}
memStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);
request.ContentLength = memStream.Length;
using (Stream requestStream = request.GetRequestStream())
{
memStream.Position = 0;
byte[] tempBuffer = new byte[memStream.Length];
memStream.Read(tempBuffer, 0, tempBuffer.Length);
memStream.Close();
requestStream.Write(tempBuffer, 0, tempBuffer.Length);
}
using (var response = request.GetResponse())
{
Stream stream2 = response.GetResponseStream();
StreamReader reader2 = new StreamReader(stream2);
return reader2.ReadToEnd();
}
}
Here the code to send file (proxy used for Roscomnadzor) Use your variables instead of msg.GetParam({""}) you'll need chat_id, bot_id,photo and caption. And "proxy" if you need it.
string filePath = msg.GetParam("photo");
string URL = "https://api.telegram.org/bot" + msg.GetParam("bot_id") + "/sendPhoto";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
WebProxy myProxy = new WebProxy(msg.GetParam("proxy"));
myProxy.UseDefaultCredentials = true;
request.Proxy = myProxy;
string[] variable_name = {filePath};
NameValueCollection form = new NameValueCollection();
form["chat_id"] = msg.GetParam("chat_id");
form["caption"] = msg.GetParam("caption");
UploadFilesToRemoteUrl(request, variable_name, form);
I am using this code below to authenticate and upload file to a website, but I am getting an error "Corrupt form data: no leading boundary".
In the Fiddler I see this, ( I see the exact same thing when I upload the file using browser), not sure why I am getting an error when I am doing using C# HttpWebRequest/WebClient
Fiddler Info:
------WebKitFormBoundary9ewWOMyBmk0YAhTL
Content-Disposition: form-data; name="FileSubmitted"; filename="SAMPLE_0001.xls"
Content-Type: application/vnd.ms-excel
------WebKitFormBoundary9ewWOMyBmk0YAhTL
Content-Disposition: form-data; name="FileSubmittedValue"
C:\path\SAMPLE_0001.xls
------WebKitFormBoundary9ewWOMyBmk0YAhTL--
Assumptions:
I hard coded the 'boundary' value, because I am not sure how to get the 'boundary' value after login.
Code: Copied from Stackoverflow
static void Main()
{
NameValueCollection nvCollection = new NameValueCollection();
CookieAwareWebClient webClient = new CookieAwareWebClient();
nvCollection.Clear();
nvCollection["Name"] = "ABC";
nvCollection["Password"] = "XYZ";
//Login to the Site
byte[] responseBytes = webClient.UploadValues("https://www.somesite.com/login.cfm", "POST", nvCollection);
string resultAuthTicket = Encoding.UTF8.GetString(responseBytes);
//Get Cookies
CookieCollection cookies = webClient.CookieContainer.GetCookies(new Uri("https://www.somesite.com/login.cfm"));
//
string URL = "https://www.somesite.com/app/request.cfm";
string boundary = "----WebKitFormBoundary9ewWOMyBmk0YAhTL";
string FilePath = "C:\\Users\\user.name\\Desktop\\SAMPLE_0001.xls";
byte[] fileData = GetMultipartFormData(new Dictionary<string, object>() { { "FileSubmitted", FilePath } }, boundary);
PostForm(URL, "", "", fileData, boundary, cookies);
}
private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
{
Encoding encoding = Encoding.UTF8;
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;
{
string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n",
boundary,
param.Key,
param.Value,
"application/vnd.ms-excel");
formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
string postData2 = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\" \r\n\r\n{2}",
boundary,
"FileSubmittedValue",
"C:\\path\\SAMPLE_0001.xls");
formDataStream.Write(encoding.GetBytes(postData2), 0, encoding.GetByteCount(postData2));
}
}
// 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;
}
private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData, string boundary, CookieCollection cookies)
{
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.UserAgent = userAgent;
request.ContentLength = formData.Length;
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.Method = "POST";
request.KeepAlive = true;
request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(cookies);
request.Host = "secure.somesite.com";
request.Referer = String.Format("https://secure.somesite.com/app/request.cfm?CFID={0}&CFTOKEN={1}", cookies[0].Value, cookies[1].Value);
request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36";
// Send the form data to the request.
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(formData, 0, formData.Length);
requestStream.Close();
}
try
{
return request.GetResponse() as HttpWebResponse;
}
catch (WebException wex)
{
var pageContent = new StreamReader(wex.Response.GetResponseStream())
.ReadToEnd();
return null;
}
return null;
}
Error:
<head><title>JRun Servlet Error</title></head><h1>500 </h1><body>
<pre>
Corrupt form data: no leading boundary: != ------WebKitFormBoundary9ewWOMyBmk0YAhTL</pre><br><pre>
java.io.IOException: Corrupt form data: no leading boundary: != ------WebKitFormBoundary9ewWOMyBmk0YAhTL
at com.oreilly.servlet.multipart.MultipartParser.<init>(MultipartParser.java:176)
at com.oreilly.servlet.multipart.MultipartParser.<init>(MultipartParser.java:95)
at coldfusion.filter.FormScope.fillMultipart(FormScope.java:170)
at coldfusion.filter.FusionContext.SymTab_initForRequest(FusionContext.java:435)
at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:33)
at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)
at coldfusion.filter.RequestThrottleFilter.invoke(RequestThrottleFilter.java:126)
at coldfusion.CfmServlet.service(CfmServlet.java:175)
at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89)
at jrun.servlet.FilterChain.doFilter(FilterChain.java:86)
at coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42)
at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46)
at jrun.servlet.FilterChain.doFilter(FilterChain.java:94)
at jrun.servlet.FilterChain.service(FilterChain.java:101)
at jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:106)
at jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42)
at jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:286)
at jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:543)
at jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:203)
at jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:428)
at jrunx.scheduler.WorkerThread.run(WorkerThread.java:66)
</pre></body>
Don't Knows: Since I copied this source code from Stackoverflow, I don't know if the file really getting uploaded to C:\Path folder on server (look at the Fiddler data).
Update: When I try to upload file using browser I see Content-Length: 49504, but when I try using C# program Content-Length: 385 (Even when I use the same file in both browser and C# program)
Also, when I try now I am getting "No data was received in the uploaded file SAMPLE_0001.xls"
The code is missing this line of code, which is the actual file content.
formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
Thanks all for your help!
I need to do a HTTP POST using C#. It needs to do a postback the same way as an IE6 page.
From the documentation the postback should look like
POST /.../Upload.asp?b_customerId=[O/M1234] HTTP/1.1
Content-length: 12345
Content-type: multipart/form-data; boundary=vxvxv
Host: www.foo.com
--vxvxv
Content-disposition: form-data; name=”File1”; filename=”noColonsSpacesOrAmpersandsInHere”
Content-type: text/xml
<?xml version=”1.0” encoding=”UTF-8”?>
...
<bat:Batch ...
.......
</bat:Batch>
--vxvxv--
I think im having trouble with the boundary characters. I tried setting the boundary in the post data and fiddler shows something similar but I get a page back with the error "Invalid procedure call or argument". the Content-disposition is in the body rather than the header to keep it within the boundaries. Im not sure that is right. Am I setting the boundary the correct way? Can anyone give some guidance on how to do an IE6 style HTTP POST using C# ? Thanks
My Code
data = "--vxvxv" + Environment.NewLine +
"Content-disposition: form-data; name=\"File1\";" + Environment.NewLine +
"filename=\"provideTest.xml\"" + Environment.NewLine +
"Content-type: text/xml" + Environment.NewLine +
#"<?xml version=""1.0"" encoding=""UTF-8""?>" + Environment.NewLine +
data + Environment.NewLine +
"--vxvxv--";
var encoding = ASCIIEncoding.UTF8;
HttpWebRequest request;
var postData = encoding.GetBytes(data);
request = (HttpWebRequest)WebRequest.Create(url);
request.ContentLength = postData.Length;
request.Method = "POST";
request.ContentType = "multipart/form-data; boundary=vxvxv";
request.Host = "www.foo.com";
request.ContentLength = postData.Length;
X509Certificate2Collection certCollect = new X509Certificate2Collection();
X509Certificate2 cert = new X509Certificate2(#"C:\a\cert.pfx", "password");
certCollect.Add(cert);
request.ClientCertificates = certCollect;
using (Stream writeStream = request.GetRequestStream()) {
writeStream.Write(postData, 0, postData.Length); }
WebResponse webResponse = request.GetResponse();
string output = new StreamReader(webResponse.GetResponseStream()).ReadToEnd();
LogEntry.Write("Recieved : " + output);
return output;
Fiddler Output (raw)
POST https://../Upload.asp?b_customerId=%5BO/M1234%5D HTTP/1.1
Content-Type: multipart/form-data; boundary=vxvxv
Host: www.foo.com
Content-Length: 5500
Expect: 100-continue
Connection: Keep-Alive
--vxvxv
Content-disposition: form-data; name="File1";
filename="provideTest.xml"
Content-type: text/xml
<?xml version="1.0" encoding="UTF-8"?>
...SNIP...
</bat:Batch>
--vxvxv--
I think that you have two potential problems:
1) The URL that you are sending formats the b_CustomerId parameter differently than the IE6 implementation. If the site you are targeting does not expect an HTML-encoded value, this could very easily be the source of the error message.
Your request:
Upload.asp?b_customerId=%5BO/M1234%5D
The IE6 request:
Upload.asp?b_customerId=[O/M1234]
In order to fix this issue, you can create a new Url from an overload of the Uri class constructor that has been marked as obsolete, but still works correctly. This overload allows you to specify that the string has already been escaped in the second parameter.
In order to use this constructor, change this line:
request = (HttpWebRequest)WebRequest.Create(url);
to this:
request = (HttpWebRequest)WebRequest.Create(new Uri(url, true));
2) The Content-disposition tag is not formatted the same way in your request as it is in the IE6 request.
Your request:
Content-disposition: form-data; name="File1";
filename="provideTest.xml"
IE6 request:
Content-disposition: form-data; name=”File1”; filename=”noColonsSpacesOrAmpersandsInHere”
This can be resolved by changing these two lines:
"Content-disposition: form-data; name=\"File1\";" + Environment.NewLine +
"filename=\"provideTest.xml\"" + Environment.NewLine +
to:
"Content-disposition: form-data; name=\"File1\"; " +
"filename=\"provideTest.xml\"" + Environment.NewLine +
I have blogged about a way of uploading multiple files using a WebClient and the possibility to send parameters as well. Here's the relevant code:
public class UploadFile
{
public UploadFile()
{
ContentType = "application/octet-stream";
}
public string Name { get; set; }
public string Filename { get; set; }
public string ContentType { get; set; }
public Stream Stream { get; set; }
}
and then a method to perform the upload:
public byte[] UploadFiles(string address, IEnumerable<UploadFile> files, NameValueCollection values)
{
var request = WebRequest.Create(address);
request.Method = "POST";
var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x", NumberFormatInfo.InvariantInfo);
request.ContentType = "multipart/form-data; boundary=" + boundary;
boundary = "--" + boundary;
using (var requestStream = request.GetRequestStream())
{
// Write the values
foreach (string name in values.Keys)
{
var buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.ASCII.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"{1}{1}", name, Environment.NewLine));
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.UTF8.GetBytes(values[name] + Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
}
// Write the files
foreach (var file in files)
{
var buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"{2}", file.Name, file.Filename, Environment.NewLine));
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.ASCII.GetBytes(string.Format("Content-Type: {0}{1}{1}", file.ContentType, Environment.NewLine));
requestStream.Write(buffer, 0, buffer.Length);
file.Stream.CopyTo(requestStream);
buffer = Encoding.ASCII.GetBytes(Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
}
var boundaryBuffer = Encoding.ASCII.GetBytes(boundary + "--");
requestStream.Write(boundaryBuffer, 0, boundaryBuffer.Length);
}
using (var response = request.GetResponse())
using (var responseStream = response.GetResponseStream())
using (var stream = new MemoryStream())
{
responseStream.CopyTo(stream);
return stream.ToArray();
}
}
which could be used like so:
using (var stream1 = File.Open("test.txt", FileMode.Open))
using (var stream2 = File.Open("test.xml", FileMode.Open))
using (var stream3 = File.Open("test.pdf", FileMode.Open))
{
var files = new[]
{
new UploadFile
{
Name = "file",
Filename = "test.txt",
ContentType = "text/plain",
Stream = stream1
},
new UploadFile
{
Name = "file",
Filename = "test.xml",
ContentType = "text/xml",
Stream = stream2
},
new UploadFile
{
Name = "file",
Filename = "test.pdf",
ContentType = "application/pdf",
Stream = stream3
}
};
var values = new NameValueCollection
{
{ "key1", "value1" },
{ "key2", "value2" },
{ "key3", "value3" },
};
byte[] result = UploadFiles("http://localhost:1234/upload", files, values);
}
This won't be a complete answer, but you could look at using a socket instead of WebRequest and performing the HTTP request yourself. It seems that the multipart handler on your server is non-conformi g and expects the request to be the same as IE6 would make, so emulating that yourself would be the best approach.
I am trying to use the ImageShack API to upload images. To use it, I am supposed to POST the image using multipart/form-data. I did it like ...
var postData = "";
var req = HttpWebRequest.Create("http://www.imageshack.us/upload_api.php");
req.Method = "POST";
req.ContentType = "multipart/form-data";
postData += "key=my_key_here&";
postData += "type=base64&";
// get base64 data from image
byte[] bytes = File.ReadAllBytes(#"D:\tmp\WpfApplication1\WpfApplication1\Images\Icon128.gif");
string encoded = Convert.ToBase64String(bytes);
postData += "fileupload=" + encoded;
byte[] reqData = Encoding.UTF8.GetBytes(postData);
using (Stream dataStream = req.GetRequestStream())
{
dataStream.Write(reqData, 0, reqData.Length);
}
var res = (HttpWebResponse)req.GetResponse();
var resStream = res.GetResponseStream();
var reader = new StreamReader(resStream);
string resString = reader.ReadToEnd();
txt1.Text = resString;
but ImageShack is complaining that
<links>
<error id="parameter_missing">Sorry, but we've detected that unexpected data is received. Required parameter 'fileupload' is missing or your post is not multipart/form-data</error>
</links>
FileUpload is present and I am using multipart/form-data whats wrong?
UPDATE:
New Code http://pastebin.com/TN6e0CD8
Post data http://pastebin.com/fYE9fsxs
UPDATE 2
i looked at the other question Multipart forms from C# client. modified my code with boundary, removed the expect 100 header still i cant get it working ...
ServicePointManager.Expect100Continue = false;
var boundary = "-----------------------------28520690214962";
var newLine = Environment.NewLine;
var propFormat = boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine +
"{1}" + newLine + newLine;
var fileHeaderFormat = boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + newLine;
var req = (HttpWebRequest)HttpWebRequest.Create("http://jm/php/upload.php");
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data; boundary=" + boundary;
using (var reqStream = req.GetRequestStream()) {
var reqWriter = new StreamWriter(reqStream);
var tmp = string.Format(propFormat, "str1", "hello world");
reqWriter.Write(tmp);
tmp = string.Format(propFormat, "str2", "hello world 2");
reqWriter.Write(tmp);
reqWriter.Write(boundary + "--");
reqWriter.Flush();
}
var res = req.GetResponse();
using (var resStream = res.GetResponseStream()) {
var reader = new StreamReader(resStream);
txt1.Text = reader.ReadToEnd();
}
I finally got it with the following code ...
var boundary = "------------------------" + DateTime.Now.Ticks;
var newLine = Environment.NewLine;
var propFormat = "--" + boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine +
"{1}" + newLine;
var fileHeaderFormat = "--" + boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + newLine;
var req = (HttpWebRequest)HttpWebRequest.Create("http://jm/php/upload.php");
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data; boundary=" + boundary;
using (var reqStream = req.GetRequestStream()) {
var reqWriter = new StreamWriter(reqStream);
var tmp = string.Format(propFormat, "str1", "hello world");
reqWriter.Write(tmp);
tmp = string.Format(propFormat, "str2", "hello world 2");
reqWriter.Write(tmp);
reqWriter.Write("--" + boundary + "--");
reqWriter.Flush();
}
var res = req.GetResponse();
using (var resStream = res.GetResponseStream()) {
var reader = new StreamReader(resStream);
txt1.Text = reader.ReadToEnd();
}
Notice boundaries have to begin with -- {boundary declared in ContentType} and ending boundary must begin & end with -- . in my case, I originally used
var propFormat = boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine +
"{1}" + newLine;
replace it with
var propFormat = "--" + boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine +
"{1}" + newLine;
and everything works
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.
This is nothing like multipart/form-data
There's no boundaries between fields (needed even with one field).
Why are you base-64 encoding?
There's no indication of the content-type of the image.
Take a look at RFC 2388 for the actual format spec. It can also be useful to look at a Fiddler grab of a file upload from a web-page.