tracking upload progress of multiple file uploads using multipart/body web request - c#

I'm using HttpWebRequest to upload files to the server. The request sends 2 files to the server, a video file and an image file. I'm trying to track the progress of entire progress but the issue is, the progress log runs separately for each file upload. I want the progress to show only once for both the uploads but I can't quite figure out how to do it. Here's my client side code:
Dictionary<string, string> fields = new Dictionary<string, string>();
fields.Add("username", username);
HttpWebRequest hr = WebRequest.Create(url) as HttpWebRequest;
hr.Timeout = 500000;
string bound = "----------------------------" + DateTime.Now.Ticks.ToString("x");
hr.ContentType = "multipart/form-data; boundary=" + bound;
hr.Method = "POST";
hr.KeepAlive = true;
hr.Credentials = CredentialCache.DefaultCredentials;
byte[] boundBytes = Encoding.ASCII.GetBytes("\r\n--" + bound + "\r\n");
string formDataTemplate = "\r\n--" + bound + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";
Stream s = hr.GetRequestStreamWithTimeout(1000000);
foreach (string key in fields.Keys)
{
byte[] formItemBytes = Encoding.UTF8.GetBytes(
string.Format(formDataTemplate, key, fields[key]));
s.Write(formItemBytes, 0, formItemBytes.Length);
}
s.Write(boundBytes, 0, boundBytes.Length);
string headerTemplate =
"Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n Content-Type: application/octet-stream\r\n\r\n";
List<string> files = new List<string> { fileUrl, thumbUrl };
List<string> type = new List<string> { "video", "thumb" };
int count = 0;
foreach (string f in files)
{
var m = Path.GetFileName(f);
var t = type[count];
var j = string.Format(headerTemplate, t, m);
byte[] headerBytes = Encoding.UTF8.GetBytes(
string.Format(headerTemplate, type[count], Path.GetFileName(f)));
s.Write(headerBytes, 0, headerBytes.Length);
FileStream fs = new FileStream(f, FileMode.Open, FileAccess.Read);
int bytesRead = 0;
long bytesSoFar = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) != 0)
{
bytesSoFar += bytesRead;
s.Write(buffer, 0, buffer.Length);
Console.WriteLine(string.Format("sending file data {0:0.000}%", (bytesSoFar * 100.0f) / fs.Length));
}
s.Write(boundBytes, 0, boundBytes.Length);
fs.Close();
count += 1;
}
s.Close();
string respString = "";
hr.BeginGetResponse((IAsyncResult res) =>
{
WebResponse resp = ((HttpWebRequest)res.AsyncState).EndGetResponse(res);
StreamReader respReader = new StreamReader(resp.GetResponseStream());
respString = respReader.ReadToEnd();
resp.Close();
resp = null;
}, hr);
while (!hr.HaveResponse)
{
Console.Write("hiya bob!");
Thread.Sleep(150);
}
Console.Write(respString);
hr = null;
How do I combine the progress log for both uploads into a single log? Any help is appreciated.

One option is to calculate the total number of bytes you need to send before doing any work:
// Calculate the total size to upload before starting work
long totalToUpload = 0;
foreach (var f in files)
{
totalToUpload += (new FileInfo(f)).Length;
}
Then keep track of the total number of bytes sent in any file, and use that in your calculation of progress:
int count = 0;
long bytesSoFar = 0;
foreach (string f in files)
{
// ... Your existing work ...
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) != 0)
{
bytesSoFar += bytesRead;
// Make sure to only write the number of bytes read from the file
s.Write(buffer, 0, bytesRead);
// Console.WriteLine takes a string.Format() style string
Console.WriteLine("sending file data {0:0.000}%", (bytesSoFar * 100.0f) / totalToUpload);
}

Related

C++ Upload large files over HTTP request, without reading whole file into the http request

My program needs to upload huge files of 1GB,2GB,etc . I have tried using QHttpMultipart request & other PUT & POST requests on QNetworkAccessManager. Every time a QNetworkRequest is build, the application memory shoots up from 20MB to 1GB or 2GB as per the file, which leads to my application crashing (WINDOWS QT APP).
{
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text\""));
textPart.setBody("my text");
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("image/jpeg"));
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"image\""));
QFile *file = new QFile("LargeFile.zip");
file->open(QIODevice::ReadOnly);
imagePart.setBodyDevice(file);
file->setParent(multiPart);
multiPart->append(textPart);
multiPart->append(imagePart);
QUrl url("http://my.server.tld");
QNetworkRequest request(url);
quint64 contentlength = file->bytesAvailable();
request.setHeader(QNetworkRequest::ContentLengthHeader,contentlength);
request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute,true);
QNetworkAccessManager manager;
QNetworkReply *reply = manager.post(request, multiPart);
multiPart->setParent(reply);
}
HUGE increase in application memory equivalent to the huge file size at this point after POST request is fired,as whole file is read & added into request body
What I found in C# is that, it's possible to upload huge files without increasing or reading the whole file. They keep on writing on the http request stream in chunks.
In this code whatever may the File size be in GBs, application memory never goes beyond 40MB.
public void upload(string url, string imagename, string imagepath, string finalType)
{
try
{
FileStream fileStream = null;
Stream rs = null;
WebResponse lobjResponse = null;
string lstrResponse = string.Empty;
bool lblnIsConnected = true;
long mlngBytesUploaded = 0;
long llngContentLength = 0;
long[] llngarrProgressStatusData = new long[2];
string pstrResponse = string.Empty;
long lintContentLenght = 0;
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
string formitem = string.Format(formdataTemplate, "abc", "abc-val");
byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
string header = string.Format(headerTemplate, imagename, imagename, finalType);
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
fileStream = new FileStream(imagepath, FileMode.Open, FileAccess.Read);
lintContentLenght += boundarybytes.Length;
lintContentLenght += formitembytes.Length;
lintContentLenght += boundarybytes.Length;
lintContentLenght += headerbytes.Length;
lintContentLenght += fileStream.Length;
lintContentLenght += trailer.Length;
wr = (HttpWebRequest)WebRequest.Create(url);
wr.AllowWriteStreamBuffering = false;
wr.ContentType = "multipart/form-data; boundary=" + boundary;
wr.ContentLength = lintContentLenght;
wr.Method = "POST";
wr.KeepAlive = true;
using (rs = wr.GetRequestStream())
{
rs.Write(boundarybytes, 0, boundarybytes.Length);
rs.Write(formitembytes, 0, formitembytes.Length);
rs.Write(boundarybytes, 0, boundarybytes.Length);
rs.Write(headerbytes, 0, headerbytes.Length);
int bytesRead = 0;
byte[] bufferSize = new byte[4096];
llngContentLength = wr.ContentLength;
llngarrProgressStatusData[0] = llngContentLength;
// BECAUSE OF READING FILE DATA in chunks & WRITING IT to the STREAM. HERE WHOLE FILE ISN'T READ & SET INTO REQUEST like in QHTTPMULTIPART request. Application memory remains constant in C# code.**
while ((bytesRead = fileStream.Read(bufferSize, 0, bufferSize.Length)) != 0)
{
if (!lblnIsConnected) break;
mlngBytesUploaded += bytesRead;
rs.Write(bufferSize, 0, bytesRead);
if (mlngBytesUploaded > llngContentLength)
mlngBytesUploaded = llngContentLength;
llngarrProgressStatusData[1] = mlngBytesUploaded;
NotifyHTTPProgressBarUpdate(mlngBytesUploaded, llngContentLength);
}
fileStream.Close();
if (lblnIsConnected)
{
rs.Write(trailer, 0, trailer.Length);
rs.Close();
}
}
}
}
Sending file chunks is an option but currently I don't have that support on my server.
Can you please advice me on similar implementation of above C# code in QT to upload file in a stream over HTTP PUT/POST on a given URL. Is there a way to do the same in c++?

Transfer really large files using WebClient

My requirement is to transfer a zip file of size 400MB or more; The following code works for at least 40MB; But for more I would have to change byte[] bytes = new byte[50000000]; to byte[] bytes = new byte[400000000]; and maxRequestLength to maxRequestLength="409600";
The problem is byte[] bytes = new byte[100000000]; returns an error regarding insufficient space. So how can I transfer large files using WebClient??
WebClient client = new WebClient();
client.AllowWriteStreamBuffering = true;
UriBuilder ub = new UriBuilder("http://localhost:57596/UploadImages.ashx");
ub.Query = "ImageName=" + "DataSet" + DataSetId + ".zip";
client.OpenWriteCompleted += (InputStream, eArguments) =>
{
try
{
using (Stream output = eArguments.Result)
{
output.Write(ImagesAux, 0, (int)ImagesAux.Length);
//numeroimagem++;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
//throw;
}
};
client.OpenWriteAsync(ub.Uri);
in UploadImages.ashx
public void ProcessRequest(HttpContext context)
{
//context.Response.ContentType = "text/plain";
//context.Response.Write("Hello World");
string ImageName = context.Request.QueryString["ImageName"];
string UploadPath = context.Server.MapPath("~/ServerImages/");
using (FileStream stream = File.Create(UploadPath + ImageName))
{
byte[] bytes = new byte[50000000]; //
int bytesToRead = 0;
while ((bytesToRead =
context.Request.InputStream.Read(bytes, 0, bytes.Length)) != 0)
{
stream.Write(bytes, 0, bytesToRead);
stream.Close();
}
}
}
in Web.config
<httpRuntime targetFramework="4.5" maxRequestLength="40960"/>
You should never load everything in memory then write all back to disk, but instead you should load pieces and write them to disk while you are reading them.
When you've done reading you close the stream you are writing to.
Otherwise as soon as you reach sizes as GB you can get an OutOfMemory really quick.
So I would change the writing bytes to disk from this:
using (FileStream stream = File.Create(UploadPath + ImageName))
{
byte[] bytes = new byte[50000000]; //
int bytesToRead = 0;
while ((bytesToRead = context.Request.InputStream.Read(bytes, 0, bytes.Length)) != 0)
{
stream.Write(bytes, 0, bytesToRead);
stream.Close();
}
}
to this:
using (FileStream stream = File.Create(UploadPath + ImageName))
{
byte[] bytes = new byte[1024];
long totalBytes = context.Request.InputStream.Length;
long bytesRead = 0;
int bytesToRead = bytes.Length;
if (totalBytes - bytesRead < bytes.Length)
bytesToRead = (int)(totalBytes - bytesRead);
bytes = new byte[bytesToRead];
while ((bytesToRead = context.Request.InputStream.Read(bytes, bytesRead, bytes.Length)) != 0)
{
stream.Write(bytes, bytesRead, bytes.Length);
bytesRead += bytes.Length;
if (totalBytes - bytesRead < bytes.Length)
bytesToRead = (int)(totalBytes - bytesRead);
bytes = new byte[bytesToRead];
}
stream.Close();
}
1024 would be the buffer size.

Receiving an out of memory exception with memorystream

I'm attempting to write an application that will work with the open source media management platform, Kaltura. Kaltura has provided some C# client libraries that talk to their web API and I have been able to talk to the server and upload videos successfully. The problem I am having is that once files reach a certain size, I receive an out of memory exception and the program crashes. I would like to attempt to fix this issue and submit the improved code back to the open source project, but being new to C#, I'm not exactly where to start. Is there a better way than memorystream to do what they're doing?
Thanks in advance.
//Problematic code
private void PostMultiPartWithFiles(HttpWebRequest request, KalturaParams kparams, KalturaFiles kfiles)
{
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
request.ContentType = "multipart/form-data; boundary=" + boundary;
// use a memory stream because we don't know the content length of the request when we have multiple files
MemoryStream memStream = new MemoryStream();
byte[] buffer;
int bytesRead = 0;
StringBuilder sb = new StringBuilder();
sb.Append("--" + boundary + "\r\n");
foreach (KeyValuePair<string, string> param in kparams)
{
sb.Append("Content-Disposition: form-data; name=\"" + param.Key + "\"" + "\r\n");
sb.Append("\r\n");
sb.Append(param.Value);
sb.Append("\r\n--" + boundary + "\r\n");
}
buffer = Encoding.UTF8.GetBytes(sb.ToString());
memStream.Write(buffer, 0, buffer.Length);
foreach (KeyValuePair<string, FileStream> file in kfiles)
{
sb = new StringBuilder();
FileStream fileStream = file.Value;
sb.Append("Content-Disposition: form-data; name=\"" + file.Key + "\"; filename=\"" + Path.GetFileName(fileStream.Name) + "\"" + "\r\n");
sb.Append("Content-Type: application/octet-stream" + "\r\n");
sb.Append("\r\n");
// write the current string builder content
buffer = Encoding.UTF8.GetBytes(sb.ToString());
memStream.Write(buffer, 0, buffer.Length);
// write the file content
buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))];
bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
memStream.Write(buffer, 0, bytesRead);
buffer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
memStream.Write(buffer, 0, buffer.Length);
}
request.ContentLength = memStream.Length;
Stream requestStream = request.GetRequestStream();
// write the memorty stream to the request stream
memStream.Seek(0, SeekOrigin.Begin);
buffer = new Byte[checked((uint)Math.Min(4096, (int)memStream.Length))];
bytesRead = 0;
while ((bytesRead = memStream.Read(buffer, 0, buffer.Length)) != 0)
requestStream.Write(buffer, 0, bytesRead);
requestStream.Close();
memStream.Close();
}
Here's a version more or less how I would write it. It only compiles, but I haven't tested it. Note the use of a StreamWriter and the direct use of the request stream...
public class SendStuff
{
private readonly HttpWebRequest _request;
private readonly Dictionary<string, string> _kparams;
private readonly Dictionary<string, FileStream> _kfiles;
readonly string _boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
public SendStuff(
HttpWebRequest request,
Dictionary<string, string> kparams,
Dictionary<string, FileStream> kfiles)
{
_request = request;
_kparams = kparams;
_kfiles = kfiles;
_request.ContentType = "multipart/form-data; boundary=" + _boundary;
}
public void Do()
{
// Based on HTTP 1.1, if the server can determine the content length, it need not insist on
// us sending one. If you are talking
// to a "special" server, construct the headers beforehand, measure their length
// and identify the file lengths of the files to be sent.
using (var reqStream = _request.GetRequestStream())
using (var writer = new StreamWriter(reqStream))
{
writer.NewLine = "\r\n";
WriteBoundary(writer);
WriteParams(writer);
foreach (var file in _kfiles)
{
writer.WriteLine("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"",
file.Key,
Path.GetFileName(file.Value.Name));
writer.WriteLine("Content-Type: application/octet-stream");
writer.WriteLine();
WriteTheFileContent(reqStream, file.Value);
WriteBoundary(writer);
}
}
}
private static void WriteTheFileContent(Stream reqStream, Stream fileStream)
{
int bytesRead;
var buffer = new byte[4096];
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
reqStream.Write(buffer, 0, bytesRead);
}
private void WriteParams(StreamWriter writer)
{
foreach (var param in _kparams)
{
writer.WriteLine("Content-Disposition: form-data; name=\"{0}\"", param.Key);
writer.WriteLine();
writer.WriteLine(param.Value);
WriteBoundary(writer);
}
}
private void WriteBoundary(TextWriter writer)
{
writer.WriteLine("\r\n--{0}\r\n", _boundary);
}
}

UploadFile with POST values by WebClient

I want to upload file to a host by using WebClient class. I also want to pass some values which should be displayed in the $_POST array on the server part (PHP). I want to do it by one connect
I've used code bellow
using (WebClient wc = new WebClient())
{
wc.Encoding = Encoding.UTF8;
NameValueCollection values = new NameValueCollection();
values.Add("client", "VIP");
values.Add("name", "John Doe");
wc.QueryString = values; // this displayes in $_GET
byte[] ans= wc.UploadFile(address, dumpPath);
}
If i've used QueryString property, the values displayed in $_GET array.But i want to send it by post method
There's nothing built-in that allows you to do that. I have blogged about an extension that you could use. Here are the relevant classes:
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; }
}
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();
}
}
and now you could use it in your application:
using (var stream = File.Open(dumpPath, FileMode.Open))
{
var files = new[]
{
new UploadFile
{
Name = "file",
Filename = Path.GetFileName(dumpPath),
ContentType = "text/plain",
Stream = stream
}
};
var values = new NameValueCollection
{
{ "client", "VIP" },
{ "name", "John Doe" },
};
byte[] result = UploadFiles(address, files, values);
}
Now in your PHP script you could use the $_POST["client"], $_POST["name"] and $_FILES["file"].
If someone wants to use #darin-dimitrov s solution in an async pattern with progress reporting, that's the way to go (for .NET 4.0):
public void UploadFileAsync(NameValueCollection values, Stream fileStream)
{
//to fire events on the calling thread
_asyncOperation = AsyncOperationManager.CreateOperation(null);
var ms = new MemoryStream();
//make a copy of the input stream in case sb uses disposable stream
fileStream.CopyTo(ms);
//you cannot set stream position often enough to zero
ms.Position = 0;
Task.Factory.StartNew(() =>
{
try
{
const string contentType = "application/octet-stream";
var request = WebRequest.Create(_url);
request.Method = "POST";
var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x", NumberFormatInfo.InvariantInfo);
request.ContentType = "multipart/form-data; boundary=" + boundary;
boundary = "--" + boundary;
var dataStream = new MemoryStream();
byte[] buffer;
// Write the values
foreach (string name in values.Keys)
{
buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine);
dataStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.ASCII.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"{1}{1}", name, Environment.NewLine));
dataStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.UTF8.GetBytes(values[name] + Environment.NewLine);
dataStream.Write(buffer, 0, buffer.Length);
}
// Write the file
buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine);
dataStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.UTF8.GetBytes($"Content-Disposition: form-data; name=\"file\"; filename=\"file\"{Environment.NewLine}");
dataStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.ASCII.GetBytes(string.Format("Content-Type: {0}{1}{1}", contentType, Environment.NewLine));
dataStream.Write(buffer, 0, buffer.Length);
ms.CopyTo(dataStream);
buffer = Encoding.ASCII.GetBytes(Environment.NewLine);
dataStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.ASCII.GetBytes(boundary + "--");
dataStream.Write(buffer, 0, buffer.Length);
dataStream.Position = 0;
//IMPORTANT: set content length to directly write to network socket
request.ContentLength = dataStream.Length;
var requestStream = request.GetRequestStream();
//Write data in chunks and report progress
var size = dataStream.Length;
const int chunkSize = 64 * 1024;
buffer = new byte[chunkSize];
long bytesSent = 0;
int readBytes;
while ((readBytes = dataStream.Read(buffer, 0, buffer.Length)) > 0)
{
requestStream.Write(buffer, 0, readBytes);
bytesSent += readBytes;
var status = "Uploading... " + bytesSent / 1024 + "KB of " + size / 1024 + "KB";
var percentage = Tools.Clamp(Convert.ToInt32(100 * bytesSent / size), 0, 100);
OnFileUploaderProgressChanged(new FileUploaderProgessChangedEventArgs(status, percentage));
}
//get response
using (var response = request.GetResponse())
using (var responseStream = response.GetResponseStream())
using (var stream = new MemoryStream())
{
// ReSharper disable once PossibleNullReferenceException - exception would get catched anyway
responseStream.CopyTo(stream);
var result = Encoding.Default.GetString(stream.ToArray());
OnFileUploaderCompleted(result == string.Empty
? new FileUploaderCompletedEventArgs(FileUploaderCompletedResult.Failed)
: new FileUploaderCompletedEventArgs(FileUploaderCompletedResult.Ok));
}
}
catch (Exception)
{
OnFileUploaderCompleted(new FileUploaderCompletedEventArgs(FileUploaderCompletedResult.Failed));
}
}, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}

This code is simple file upload.How to upload multi files with using this way.thx

public static void HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc)
{
Console.WriteLine(string.Format("Uploading {0} to {1}", file, url));
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url);
wr.ContentType = "multipart/form-data; boundary=" + boundary;
wr.Method = "POST";
wr.KeepAlive = true;
wr.Credentials = System.Net.CredentialCache.DefaultCredentials;
Stream rs = wr.GetRequestStream();
string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
foreach (string key in nvc.Keys)
{
rs.Write(boundarybytes, 0, boundarybytes.Length);
string formitem = string.Format(formdataTemplate, key, nvc[key]);
byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
rs.Write(formitembytes, 0, formitembytes.Length);
}
rs.Write(boundarybytes, 0, boundarybytes.Length);
string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
string header = string.Format(headerTemplate, paramName, file, contentType);
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
rs.Write(headerbytes, 0, headerbytes.Length);
FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[4096];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
rs.Write(buffer, 0, bytesRead);
}
fileStream.Close();
byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
rs.Write(trailer, 0, trailer.Length);
rs.Close();
WebResponse wresp = null;
try
{
wresp = wr.GetResponse();
Stream stream2 = wresp.GetResponseStream();
StreamReader reader2 = new StreamReader(stream2);
Console.WriteLine(string.Format("File uploaded, server response is: {0}", reader2.ReadToEnd()));
}
catch (Exception ex)
{
Console.WriteLine("Error uploading file", ex.Message);
if (wresp != null)
{
wresp.Close();
wresp = null;
}
}
finally
{
wr = null;
}
}
static void Main(string[] args)
{
String token = args[0];
NameValueCollection nvc = new NameValueCollection();
nvc.Add("token", token);
nvc.Add("name", "mms-deneme");
nvc.Add("frame_count", "1");
nvc.Add("frame_1_text", "1. resim text");
nvc.Add("frame_1_duration", "15");
HttpUploadFile("https://api.turkcell.hedeflimesaj.com/mms.json",
#"C:\test\test.jpg", "frame_1_visual", "image/jpeg", nvc);
}
}
}
You may checkout the following blog post I wrote on this subject.
UPDATE:
Posting the source code as the link didn't work for the OP:
Have you ever been in a situation where you needed to upload multiple files to a remote host and pass additional parameters in the request? Unfortunately there's nothing in the BCL that allows us to achieve this out of the box.
We have the UploadFile method but it is restricted to a single file and doesn't allow us to pass any additional parameters. So let's go ahead and write such method. The important part is that this method must comply with RFC 1867 so that the remote web server can successfully parse the information.
First we define a model representing a single file to be uploaded:
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 here's a sample UploadFiles method implementation:
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();
}
}
And here's a sample usage:
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);
}
In this example we are uploading 3 values and 3 files to the remote host.

Categories

Resources