[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 9932
  • Last Modified:

Sending a file to a remote server (C#.NET) using HTTP POST

I need help with sending a file to a remote server using a POST request (along with other either POST and/or GET data).
I have researched this an the only thing that does both (GET and POST at the same time) I have found is cURL.
I have used cURL in the past with PHP and it has been very easy to implement. With .NET (using cURL .NET library http://curl.haxx.se/libcurl/dotnet/) however I'm running into an issue where I am unable to "attach" the file to the HTTP POST request. If there are alternate methods for sending data (including a file) to a remote server (where I have a receiving scipt expecting a POST request) without using cURL please let me know.

Below I have a working code that takes a variable named "fields" containing a series of key=value pairs connected with ampersand (same as URI query string): key1=value1&key2=value2&...
The problem I am running into here is how to get a file across using this method in .NET. Again if there are other (better?) ways of accomplishing this please do let me know.

On the other end (the remote server) I have a script that processes the received date (as if it was submitted using a standard HTML form via HTTP POST) and the additional variables (submitted as the HTTP GET variables).
If I just use a file name for the CURLOPT_POSTFIELDS option as below I only get the file name as the variable - no file though:

easy.SetOpt(CURLoption.CURLOPT_POSTFIELDS, "myFile=myfile.txt");

If I add the contents as in the script below, I see the variable but no file or file name. So seems like I am missing on how the file stream needs to be formatted or something.

On the receiving end the script generates also a log file with all headers and data it receives along with attempting to receive the submitted file for confirmation purposes.

Any help would be greatly appreciated!
// This is in C#.NET - which works except for the file transfer part
string url = "https://myreceivingservice.com/requestResponse.php";
string fileLocation = "C:\\myUploadFolder\\myFile.txt";
string fields = "?key1=value1&key2=value2";
 
System.IO.StreamReader sr = new System.IO.StreamReader(fileLocation);
fileContents = sr.ReadToEnd();
sr.Close();
 
CURLcode context = Curl.GlobalInit((int)CURLinitFlag.CURL_GLOBAL_ALL);
Easy easy = new Easy();
easy.SetOpt(CURLoption.CURLOPT_URL, url);
easy.SetOpt(CURLoption.CURLOPT_PORT, 443);
easy.SetOpt(CURLoption.CURLOPT_POST, 1);
easy.SetOpt(CURLoption.CURLOPT_POSTFIELDS, "myFile=" + fileContents);
easy.SetOpt(CURLoption.CURLOPT_URL, url + "?" + fields);
easy.SetOpt(CURLoption.CURLOPT_FOLLOWLOCATION, 1);
 
 
// This is how I have used it in PHP - which works including the file transfer part
$url = "https://myreceivingservice.com/requestResponse.php";
$fileLocation = "C:\myUploadFolder\myFile.txt";
$fields = "?key1=value1&key2=value2";
 
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, array("myFile"=>"@$fileLocation");
curl_setopt($ch, CURLOPT_URL, $url . $fields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$postResult = curl_exec($ch);
curl_close($ch);
print $postResult;

Open in new window

0
ttepandi
Asked:
ttepandi
  • 4
  • 3
1 Solution
 
aibusinesssolutionsCommented:
Do you need all of that extra stuff?  asp.net has a class called WebClient.

System.Net.WebClient wc = new System.Net.WebClient;
wc.UploadFile("https://myreceivingservice.com/requestResponse.php", "POST", "C:\myUploadFolder\myFile.txt")
0
 
ttepandiAuthor Commented:
That's crazy - I looked at this and completely dismissed it. No, I do not need all of that extra stuff, just passing GET variables and sending a file.

I have tested this out now and here is where it stands.
The receiving script is being called but it does not see the request as a POST request.  Do I need to add some special headers? I attempted MSDN suggested headers but no luck.

wc.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");


0
 
aibusinesssolutionsCommented:
Yeah, I forgot to include the header you need.

WebClient webClient = new WebClient();                  
webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

For a GET, you can do something like this.
string postData = String.Format("value1={1}&value2={2}", variable1, variable2);
byte[] response = webClient.UploadData("the url", "GET", Encoding.ASCII.GetBytes(postData));
0
Veeam Disaster Recovery in Microsoft Azure

Veeam PN for Microsoft Azure is a FREE solution designed to simplify and automate the setup of a DR site in Microsoft Azure using lightweight software-defined networking. It reduces the complexity of VPN deployments and is designed for businesses of ALL sizes.

 
aibusinesssolutionsCommented:
Actually, after looking in to the WebClient a little more, it looks like it doesn't support multipart/form-data, which is what you need to send variables along with a file.  But, you can use HttpWebRequest.  After digging around some I found this function.  Enjoy.
private void UploadFilesToRemoteUrl(string url, string[] files, string
logpath, NameValueCollection nvc)
{
 
long length = 0;
string boundary = "----------------------------" +
DateTime.Now.Ticks.ToString("x");
 
 
HttpWebRequest httpWebRequest2 = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest2.ContentType = "multipart/form-data; boundary=" +
boundary;
httpWebRequest2.Method = "POST";
httpWebRequest2.KeepAlive = true;
httpWebRequest2.Credentials =
System.Net.CredentialCache.DefaultCredentials;
 
 
 
Stream memStream = new System.IO.MemoryStream();
 
byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" +
boundary + "\r\n");
 
 
string formdataTemplate = "\r\n--" + boundary +
"\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";
 
foreach(string key in nvc.Keys)
{
string formitem = string.Format(formdataTemplate, key, nvc[key]);
byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
memStream.Write(formitembytes, 0, formitembytes.Length);
}
 
 
memStream.Write(boundarybytes,0,boundarybytes.Leng th);
 
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++)
{
 
string header = string.Format(headerTemplate,"file"+i,files[i]);
 
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
 
memStream.Write(headerbytes,0,headerbytes.Length);
 
 
FileStream fileStream = new FileStream(files[i], FileMode.Open,
FileAccess.Read);
byte[] buffer = new byte[1024];
 
int bytesRead = 0;
 
while ( (bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0 )
{
memStream.Write(buffer, 0, bytesRead);
 
}
 
 
memStream.Write(boundarybytes,0,boundarybytes.Leng th);
 
 
fileStream.Close();
}
 
httpWebRequest2.ContentLength = memStream.Length;
 
Stream requestStream = httpWebRequest2.GetRequestStream();
 
memStream.Position = 0;
byte[] tempBuffer = new byte[memStream.Length];
memStream.Read(tempBuffer,0,tempBuffer.Length);
memStream.Close();
requestStream.Write(tempBuffer,0,tempBuffer.Length );
requestStream.Close();
 
 
WebResponse webResponse2 = httpWebRequest2.GetResponse();
 
Stream stream2 = webResponse2.GetResponseStream();
StreamReader reader2 = new StreamReader(stream2);
 
 
MessageBox.Show(reader2.ReadToEnd());
 
webResponse2.Close();
httpWebRequest2 = null;
webResponse2 = null;
 
}

Open in new window

0
 
ttepandiAuthor Commented:
OK. Thank you very much!
I have tried out this code now and I was not able to get this to work. I have made sure that there are no access issues but still for some reason the filestream is not reaching the receiving script. The receiving PHP script does get the POST variables now but still now file.

I have replaced the "Receiving" script with one in C# on the same machine where the "Posting" script resides; same domain, same folder for testing purposes to eliminate any cross-domain or access rights issues but still no dice. I am getting now "Object reference not set to an instance of an object" error. The file is not reaching the receiving script and I cannot figure out why. There's got to be something I am doing wrong on the sending side.

Getting access to the GET variables is not a problem no matter what method I use. I found that I can also just append the GET variables to the end of the URL when using the WebClient method="POST" route for example.

I will post my sample code on using the above if you don't mind looking at it.
0
 
ttepandiAuthor Commented:
Turns out I was not paying attention. There might be a more straightforward way of figuring this out, but I was specifying the filename reference incorrectly: it should have been Request.Files["file0"].FileName

using the ploadFilesToRemoteUrl method it sets the filenames in the headers as "file" + i, where i is the index and starts at 0.

Below is a working code sample using the UploadFilesToRemoteUrl method specified above.

Hope this helps someone searching to upload files to a remote script.

Thank you for your help!
//postSend.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
    string url = "http://mydomain.com/postReceive.aspx";
    string fileName = "MyTestFile.csv";
    string path = System.IO.Path.GetFullPath("C:\\test\\filesToSend\\");
    NameValueCollection myCol = new NameValueCollection();
    myCol.Add("fileName", fileName);
    string[] files = new string[1];
    files[0] = path + fileName;
    UploadFilesToRemoteUrl(url, files, "", myCol);
}
 
// postReceive.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
    string savePath = "C:\\test\\filesReceived\\";
    fileName = System.IO.Path.GetFileName(Request.Files["file0"].FileName);
    Request.Files["file0"].SaveAs(savePath + "MyNew_" + fileName);
}

Open in new window

0
 
ttepandiAuthor Commented:
Thank you for your help!
0

Featured Post

Get free NFR key for Veeam Availability Suite 9.5

Veeam is happy to provide a free NFR license (1 year, 2 sockets) to all certified IT Pros. The license allows for the non-production use of Veeam Availability Suite v9.5 in your home lab, without any feature limitations. It works for both VMware and Hyper-V environments

  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now