Async FTP Upload List of Files - c#

I'm trying to loop through a list of images and ftp them to a server. It's partially working except for it is still blocking my UI thread. Even though my ftp function is async, I'm guessing because my calling method is not, I'm not getting the results I'm looking for. Here's what I got. What am I doing wrong?
public void UploadPictures()
{
//loop through each picture and upload
for (int i = 0; i < this.items.Count; i++) {
byte[] bytes;
if (System.IO.Path.GetExtension (this.items [i].FileName.ToUpper()) == ".JPG") {
using (var imageData = this.items[i].Image.AsJPEG())
{
bytes = new byte[imageData.Length];
Marshal.Copy(imageData.Bytes, bytes, 0, Convert.ToInt32(imageData.Length));
}
var test=UploadPhoto(bytes, this.items[i].FileName);
}
if (System.IO.Path.GetExtension (this.items [i].FileName.ToUpper()) == ".PNG") {
using (var imageData = this.items[i].Image.AsPNG())
{
bytes = new byte[imageData.Length];
Marshal.Copy(imageData.Bytes, bytes, 0, Convert.ToInt32(imageData.Length));
}
var test=UploadPhoto(bytes, this.items[i].FileName);
}
}
}
public static async Task<string> UploadPhoto(byte[] photoBytes, string filename)
{
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create("ftp://XXXXXXXX/" + filename);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential ("user", "pass");
request.UseBinary = true;
request.ContentLength = photoBytes.Length;
using (Stream s = request.GetRequestStream())
{
s.Write(photoBytes, 0,photoBytes.Length);
}
WebResponse ftpResp = await (Task<WebResponse>)request.GetResponseAsync ();
return ftpResp.ToString();
}

Mark UploadPictures as async and await on UploadPhoto.

So I called my UploadPictures function using Task run and that worked. Not sure if thats the most correct way to do it.
Task.Run( () =>
{
UploadPictures();
});

Related

Web API service hangs on reading the stream

Description: I am modifying the ASP.NET Core Web API service (hosted in Windows Service) that supports resumable file uploads. This works fine and resumes file uploads in many failure conditions except one described below.
Problem: When the service is on ther other computer and the client is on mine and I unplug the cable on my computer, the client detects the absence of network while the service hangs on fileSection.FileStream.Read(). Sometimes the service detects the failure in 8 min, sometimes in 20, sometimes never.
I also noticed that after I unplug cable and stop the client, the service becomes stuck at Read() function and the file size is x KB, but when the service finally detects the exception some time later, it writes additional 4 KB to the file. This is weird because I turned off buffering and the buffer size is 2 KB.
Question: How to properly detect the absence of network on the service, or timeout properly, or cancel the request
The service code:
public static async Task<List<(Guid, string)>> StreamFileAsync(
this HttpRequest request, DeviceId deviceId, FileTransferInfo transferInfo)
{
var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType), DefaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var section = await reader.ReadNextSectionAsync(_cancellationToken);
if (section != null)
{
var fileSection = section.AsFileSection();
var targetPath = transferInfo.FileTempPath;
try
{
using (var outfile = new FileStream(transferInfo.FileTempPath, FileMode.Append, FileAccess.Write, FileShare.None))
{
var buffer = new byte[DefaultCopyBufferSize];
int read;
while ((read = fileSection.FileStream.Read(buffer, 0, buffer.Length)) > 0) // HANGS HERE
{
outfile.Write(buffer, 0, read);
transferInfo.BytesSaved = read + transferInfo.BytesSaved;
}
}
}
catch (Exception e)
{
...
}
}
}
The client code:
var request = CreateRequest(fileTransferId, boundary, header, footer, filePath, offset, headers, null);
using (Stream formDataStream = request.GetRequestStream())
{
formDataStream.ReadTimeout = 60000;
formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, header.Length);
byte[] buffer = new byte[2048];
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
fs.Seek(offset, SeekOrigin.Begin);
for (int i = 0; i < fs.Length - offset;)
{
int k = await fs.ReadAsync(buffer, 0, buffer.Length);
if (k > 0)
{
await Task.Delay(100);
await formDataStream.WriteAsync(buffer, 0, k);
}
i = i + k;
}
}
formDataStream.Write(footer, 0, footer.Length);
}
var uploadingResult = request.GetResponse() as HttpWebResponse;
private static HttpWebRequest CreateRequest(
Guid fileTransferId,
string boundary,
string header,
byte[] footer,
string filePath,
long offset,
NameValueCollection headers,
Dictionary<string, string> postParameters)
{
var url = $"{_BaseAddress}v1/ResumableUpload?fileTransferId={fileTransferId}";
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "multipart/form-data; boundary=\"" + boundary + "\"";
request.UserAgent = "Agent 1.0";
request.Headers.Add(headers); // custom headers
request.Timeout = 120000;
request.KeepAlive = true;
request.AllowReadStreamBuffering = false;
request.ReadWriteTimeout = 120000;
request.AllowWriteStreamBuffering = false;
request.ContentLength = CalculateContentLength(filePath, offset, header, footer, postParameters, boundary);
return request;
}
What I tried:
I added these in into config files:
Tried to set timeout on the server
var host = new WebHostBuilder().UseKestrel(o => { o.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);})
Used async and non-async Read()
Tried with keep alive and without
Tried to abort the request when network was restored: request?.Abort();
Tried to set formDataStream.ReadTimeout = 60000;
Since I did not find a better way, I decided to add a timeout to the reading stream and saving it to the file. The good example was posted here: https://blogs.msdn.microsoft.com/pfxteam/2012/10/05/how-do-i-cancel-non-cancelable-async-operations/
public static async Task<List<(Guid, string)>> StreamFileAsync(this HttpRequest request, DeviceId deviceId, FileTransferInfo transferInfo)
{
var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType), DefaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var section = await reader.ReadNextSectionAsync(_cancellationToken);
if (section != null)
{
var fileSection = section.AsFileSection();
var targetPath = transferInfo.FileTempPath;
try
{
await SaveMyFile(...);
}
catch (OperationCanceledException){...}
catch (Exception){...}
}
}
private static async Task SaveMyFile(...)
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(myOtherCancellationToken);
cts.CancelAfter(streamReadTimeoutInMs);
var myReadTask = StreamFile(transferInfo, fileSection, cts.Token);
await ExecuteMyTaskWithCancellation(myReadTask, cts.Token);
}
private static async Task<T> ExecuteMyTaskWithCancellation<T>(Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).TrySetResult(true), tcs))
{
if (task != await Task.WhenAny(task, tcs.Task))
{
throw new OperationCanceledException(cancellationToken);
}
}
return await task;
}
private static async Task<bool> StreamFile(...)
{
using (var outfile = new FileStream(transferInfo.FileTempPath, FileMode.Append, FileAccess.Write, FileShare.None))
{
var buffer = new byte[DefaultCopyBufferSize];
int read;
while ((read = await fileSection.FileStream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
{
if (token.IsCancellationRequested)
{
break;
}
await outfile.WriteAsync(buffer, 0, read);
transferInfo.BytesSaved = read + transferInfo.BytesSaved;
}
return true;
}
}

Monitoring upload progress with HttpWebRequest

I've recently written a C# function that does a multi part form post for uploading files. To track the progress, I'd write the form data to the request stream at 4096 bytes at a time and call back with each write. However, it seems that the request does not even get sent until GetResponseAsync() is called.
If this is the case, is the reporting of every 4096 bytes written to the request stream an accurate reporting of upload progress?
If not, how can I accurately report progress? WebClient is out of the question for me, this is in a PCL Xamarin project.
private async Task<string> PostFormAsync (string postUrl, string contentType, byte[] formData)
{
try {
HttpWebRequest request = WebRequest.Create (postUrl) as HttpWebRequest;
request.Method = "POST";
request.ContentType = contentType;
request.Headers ["Cookie"] = Constants.Cookie;
byte[] buffer = new byte[4096];
int count = 0;
int length = 0;
using (Stream requestStream = await request.GetRequestStreamAsync ()) {
using (Stream inputStream = new MemoryStream (formData)) {
while ((count = await inputStream.ReadAsync (buffer, 0, buffer.Length)) > 0) {
await requestStream.WriteAsync (buffer, 0, count);
length += count;
Device.BeginInvokeOnMainThread (() => {
_progressBar.Progress = length / formData.Length;
});
}
}
}
_progressBar.Progress = 0;
WebResponse resp = await request.GetResponseAsync ();
using (Stream stream = resp.GetResponseStream ()) {
StreamReader respReader = new StreamReader (stream);
return respReader.ReadToEnd ();
}
} catch (Exception e) {
Debug.WriteLine (e.ToString ());
return String.Empty;
}
}
Please note that I am asking about monitoring progress of an upload at 4096 bytes at a time, not a download
I ended up accomplishing this by setting the AllowWriteStreamBuffering property of the WebRequest equal to false and the SendChunked property to true.
HOWEVER Xamarin.PCL (Profile 78) does not allow you to access these properties of the HttpWebRequest, so I had to instantiate my HttpWebRequest and return it from a dependency service in my platform specific project (only tested in iOS).
public class WebDependency : IWebDependency
{
public HttpWebRequest GetWebRequest(string uri)
{
var request = WebRequest.Create (uri) as HttpWebRequest;
request.SendChunked = true;
request.AllowWriteStreamBuffering = false;
return request;
}
}
And then to instantiate my web request -
HttpWebRequest request = DependencyService.Get<IWebDependency>().GetWebRequest(uri);

how to return a xlsx file using memorystream web api

I am successfully making a web request, creating xlsx file and saving it to a directory. I would like to stream it and return it to the client without actually saving it on the server.
this is what i am currently using, it works fine
private string generateStudyTemplate(string requestId)
{
var serviceUrl = ConfigurationManager.AppSettings["serviceUrl"];
// create webRequest
HttpWebRequest webRequest = createWebRequest(serviceUrl + "/" + requestId);
// begin async call to web request
IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null);
// suspend this thread until call is complete. You might want to
// do something usefull here like update your UI
asyncResult.AsyncWaitHandle.WaitOne();
// get the response from the completed web request
var filename = string.Format("{0}.xlsx", "NewWorkbook");
string physicalPath = HttpContext.Current.Server.MapPath("/FilesForExport");
string relativePath = Path.Combine(physicalPath, filename).Replace("\\", "/");
var filePath = relativePath;
// var filePath = directory + "\\NewWorkbook.xlsx";
using (WebResponse webResponse = webRequest.EndGetResponse(asyncResult))
{
var str = webResponse.GetResponseStream();
var inBuf = new byte[webResponse.ContentLength];
var bytesToRead = Convert.ToInt32(inBuf.Length);
var bytesRead = 0;
while (bytesToRead > 0)
{
var n = str.Read(inBuf, bytesRead, bytesToRead);
if (n == 0)
break;
bytesRead += n;
bytesToRead -= n;
}
var fstr = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
fstr.Write(inBuf, 0, bytesRead);
fstr.Close();
}
return filePath;
}
private static HttpWebRequest createWebRequest(string url)
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.ContentType = "text/xml;charset=\"utf-8\"";
webRequest.Accept = "text/xml";
webRequest.Method = "GET";
return webRequest;
}
Here is what I have put together from some other examples.
public HttpResponseMessage GenerateMarketStudyResult([FromBody]Result id)
{
if (id.requestId == null)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest));
}
var serviceUrl = ConfigurationManager.AppSettings["serviceUrl"];
var streamContent = new PushStreamContent((outputStream, httpContext, transportContent) =>
{
try
{
HttpWebRequest webRequest = createWebRequest(serviceUrl + "/" + id.requestId);
IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null);
asyncResult.AsyncWaitHandle.WaitOne();
using (WebResponse webResponse = webRequest.EndGetResponse(asyncResult))
{
using (MemoryStream memoryStream = new MemoryStream())
{
var str = webResponse.GetResponseStream();
var inBuf = new byte[webResponse.ContentLength];
var bytesToRead = Convert.ToInt32(inBuf.Length);
var bytesRead = 0;
while (bytesToRead > 0)
{
var n = str.Read(inBuf, bytesRead, bytesToRead);
if (n == 0)
break;
bytesRead += n;
bytesToRead -= n;
}
memoryStream.Write(inBuf, 0, bytesRead);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
outputStream.Close();
}
});
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
streamContent.Headers.ContentDisposition.FileName = "reports.xlsx";
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = streamContent
};
return result;
}
I am not getting any exceptions, but the xlsx file is returning with 0bytes.
The breakpoint here
memoryStream.Write(inBuf, 0, bytesRead);
here is the javascript serving the returned file
$http.post('/api/GenerateMarketStudyResult/', Result, { responseType: 'arraybuffer' })
.success(function (response, status, headers, config) {
saveAs(new Blob([response], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }), 'reports.xlsx');
})
shows that the
bytesRead = 112336
I assume that you write a web service which acts as a proxy between your JavaScript and some third party web service.
First of all, if you use at least .NET 4.0, you can use the Stream.CopyTo method to copy a stream to another.
So instead of this:
using (WebResponse webResponse = webRequest.EndGetResponse(asyncResult))
{
var str = webResponse.GetResponseStream();
var inBuf = new byte[webResponse.ContentLength];
var bytesToRead = Convert.ToInt32(inBuf.Length);
var bytesRead = 0;
while (bytesToRead > 0)
{
var n = str.Read(inBuf, bytesRead, bytesToRead);
if (n == 0) break;
bytesRead += n;
bytesToRead -= n;
}
var fstr = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
fstr.Write(inBuf, 0, bytesRead);
fstr.Close();
}
You could write:
using (var webResponse = webRequest.EndGetResponse(asyncResult))
using (var fstr = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
{
webResponse.GetResponseStream().CopyTo(fstr);
}
Second, assuming you use WCF to build a web service, you could pipe the response to a memory stream, and return it. (dont forget to reset the stream's position after you finished writing)
Put together:
[WebGet(UriTemplate = "GenerateMarketStudyResult/{id}")]
public Stream GenerateMarketStudyResult(string id)
{
var serviceUrl = ConfigurationManager.AppSettings["serviceUrl"];
// create webRequest
HttpWebRequest webRequest = createWebRequest(serviceUrl + "/" + id);
// begin async call to web request
IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null);
// suspend this thread until call is complete. You might want to
// do something usefull here like update your UI
asyncResult.AsyncWaitHandle.WaitOne();
var memStream = new MemoryStream();
// var filePath = directory + "\\NewWorkbook.xlsx";
using (WebResponse webResponse = webRequest.EndGetResponse(asyncResult))
{
webResponse.GetResponseStream().CopyTo(memStream);
}
memStream.Position = 0;
var response = WebOperationContext.Current.OutgoingResponse;
response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
response.ContentLength = (int)memStream.Length;
return memStream;
}
EDIT:
You didn't copy the contents of the memoryStream to the outputStream. You can omit the memoryStream. Try this:
public HttpResponseMessage GenerateMarketStudyResult([FromBody]Result id)
{
if (id.requestId == null)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest));
}
var serviceUrl = ConfigurationManager.AppSettings["serviceUrl"];
var streamContent = new PushStreamContent((outputStream, httpContext, transportContent) =>
{
try
{
HttpWebRequest webRequest = createWebRequest(serviceUrl + "/" + id.requestId);
IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null);
asyncResult.AsyncWaitHandle.WaitOne();
using (WebResponse webResponse = webRequest.EndGetResponse(asyncResult))
{
webResponse.GetResponseStream().CopyTo(outputStream);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
outputStream.Close();
}
});
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
streamContent.Headers.ContentDisposition.FileName = "reports.xlsx";
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = streamContent
};
return result;
}

How To Use HttpWebRequest/Response To Download A Binary (.exe) File From A Web Server?

I am writing a program that needs to download an .exe file from a website and then save it to the hard drive. The .exe is stored on my site and it's url is as follows (it's not the real uri just one I made up for the purpose of this question):
http://www.mysite.com/calc.exe
After many web searches and fumbling through examples here is the code I have come up with so far:
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(http://www.mysite.com/calc.exe);
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
Stream responseStream = webResponse.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream);
string s = streamReader.ReadToEnd();
As you can see I am using the StreamReader class to read the data. After calling ReadToEnd does the stream reader contain the (binary) content of my .exe? Can I just write the content of the StreamReader to a file (named calc.exe) and I will have succesfully downloaded the .exe?
I am wondering why StreamReader ReadToEnd returns a string. In my case would this string be the binary content of calc.exe?
WebClient is the best method to download file. But you can use the following method to download a file asynchronously from web server.
private static void DownloadCurrent()
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("[url to download]");
webRequest.Method = "GET";
webRequest.Timeout = 3000;
webRequest.BeginGetResponse(new AsyncCallback(PlayResponeAsync), webRequest);
}
private static void PlayResponeAsync(IAsyncResult asyncResult)
{
long total = 0;
long received = 0;
HttpWebRequest webRequest = (HttpWebRequest)asyncResult.AsyncState;
try
{
using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.EndGetResponse(asyncResult))
{
byte[] buffer = new byte[1024];
FileStream fileStream = File.OpenWrite("[file name to write]");
using (Stream input = webResponse.GetResponseStream())
{
total = input.Length;
int size = input.Read(buffer, 0, buffer.Length);
while (size > 0)
{
fileStream.Write(buffer, 0, size);
received += size;
size = input.Read(buffer, 0, buffer.Length);
}
}
fileStream.Flush();
fileStream.Close();
}
}
catch (Exception ex)
{
}
}
There is a similar thread here - how to download the file using httpwebrequest
StreamReader is a text reader implementation i.e. it should be used to read text data and not binary data. In your case, you should be directly using the underlying response stream.
For downloading file, the simplest way would be to use WebClient.DownloadFile method.
This should directly save the file on your hard disk.
using System.Net;
using (WebClient webClient = new WebClient ())
{
webClient.DownloadFile("http://www.mysite.com/calc.exe", "calc.exe");
}
Instead of using StreamReader, you should really call Read() method of your Stream object. That will ask you for a byte[] buffer to be fill with read data, which you can then write to disk using StreamWriter or FileStream.
I'm probably a little bit late but I had the same problem with files being always 0kb big if not running in Debug mode.. This might be a relatively simple answer but disabling "DEBUG-Constants" under Properties solved it for me.
I have created a class with events so you can track download progress:
using System;
using System.IO;
using System.Net;
using System.Net.Mime;
//event examples: https://www.tutorialsteacher.com/csharp/csharp-event
public class HttpWebRequestDownload
{
private long _totalBytesLength = 0;
private long _transferredBytes = 0;
private int _transferredPercents => (int)((100 * _transferredBytes) / _totalBytesLength);
private string _defaultDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
public string downloadedFilePath = String.Empty;
public HttpWebRequestDownload(){
//Windows 7 fix
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
}
public void DownloadFile(string url, string destinationDirectory = default)
{
string filename = "";
if (destinationDirectory == default)
destinationDirectory = _defaultDirectory;
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Headers.Add("Cache-Control", "no-cache");
request.Headers.Add("Cache-Control", "no-store");
request.Headers.Add("Cache-Control", "max-age=1");
request.Headers.Add("Cache-Control", "s-maxage=1");
request.Headers.Add("Pragma", "no-cache");
request.Headers.Add("Expires", "-1");
if (!Directory.Exists(destinationDirectory))
{
Directory.CreateDirectory(destinationDirectory);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponseAsync().Result)
{
_totalBytesLength = response.ContentLength;
string path = response.Headers["Content-Disposition"];
if (string.IsNullOrWhiteSpace(path))
{
var uri = new Uri(url);
filename = Path.GetFileName(uri.LocalPath);
}
else
{
ContentDisposition contentDisposition = new ContentDisposition(path);
filename = contentDisposition.FileName;
}
using (Stream responseStream = response.GetResponseStream())
using (FileStream fileStream = File.Create(System.IO.Path.Combine(destinationDirectory, filename)))
{
byte[] buffer = new byte[1024*1024]; // 1MB buffer
ProgressEventArgs eventArgs = new ProgressEventArgs(_totalBytesLength);
int size = responseStream.Read(buffer, 0, buffer.Length);
while (size > 0)
{
fileStream.Write(buffer, 0, size);
_transferredBytes += size;
size = responseStream.Read(buffer, 0, buffer.Length);
eventArgs.UpdateData(_transferredBytes, _transferredPercents);
OnDownloadProgressChanged(eventArgs);
}
}
}
downloadedFilePath = Path.Combine(destinationDirectory, filename);
OnDownloadFileCompleted(EventArgs.Empty);
}
catch (Exception e)
{
OnError($"{e.Message} => {e?.InnerException?.Message}");
}
}
//events
public event EventHandler<ProgressEventArgs> DownloadProgressChanged;
public event EventHandler DownloadFileCompleted;
public event EventHandler<string> Error;
public class ProgressEventArgs : EventArgs
{
public long TransferredBytes { get; set; }
public int TransferredPercents { get; set; }
public long TotalBytesLength { get; set; }
public ProgressEventArgs(long transferredBytes, int transferredPercents, long totalBytesLength)
{
TransferredBytes = transferredBytes;
TransferredPercents = transferredPercents;
TotalBytesLength = totalBytesLength;
}
public ProgressEventArgs(long totalBytesLength)
{
this.TotalBytesLength = totalBytesLength;
}
public void UpdateData(long transferredBytes, int transferredPercents)
{
TransferredBytes = transferredBytes;
TransferredPercents = transferredPercents;
}
}
protected virtual void OnDownloadProgressChanged(ProgressEventArgs e)
{
DownloadProgressChanged?.Invoke(this, e);
}
protected virtual void OnDownloadFileCompleted(EventArgs e)
{
DownloadFileCompleted?.Invoke(this, e);
}
protected virtual void OnError(string errorMessage)
{
Error?.Invoke(this, errorMessage);
}
}
Here is testing example:
static void Main(string[] args)
{
HttpWebRequestDownload hDownload = new HttpWebRequestDownload();
string downloadUrl = "http://speedtest.tele2.net/10MB.zip";
hDownload.DownloadProgressChanged += HDownloadOnDownloadProgressChanged;
hDownload.DownloadFileCompleted += delegate(object o, EventArgs args)
{
Debug.WriteLine("Download finished and saved to: "+hDownload.downloadedFilePath);
};
hDownload.Error += delegate(object o, string errMessage) { Debug.WriteLine("Error has occured !! => "+errMessage); };
hDownload.DownloadFile(downloadUrl);
}
private void HDownloadOnDownloadProgressChanged(object sender, HttpWebRequestDownload.ProgressEventArgs e)
{
Debug.WriteLine("progress: "+e.TransferredBytes+" => "+e.TransferredPercents);
}

Silverlight Http Post to upload images

I have been trying to perform an HTTP Post request to upload an image in a silverlight application for a windows phone 7 application. The sample codes online do not get me the desired response from the API. Could anyone please provide a working code which does this?
By desired response I mean that the API responds saying that the uploaded file is in a format which cannot be read.
Thanks in advance!
Here is my code:
private void post_image(version, username,password,job-id, serviceUri)
{
if (session_free.bLoggedIn)
{
bool submit_success = false;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(serviceUri));
IsolatedStorageFileStream stream = IsolatedStorageFile.GetUserStoreForApplication().OpenFile("file.jpg", FileMode.Open);
request.PostMultiPartAsync(new Dictionary<string, object> { { "version", version }, { "username", user }, { "password", pass }, { filename, stream } }, new AsyncCallback(asyncResult =>
{
Thread.Sleep(1000);
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
Post_Result = reader.ReadToEnd();
this.Dispatcher.BeginInvoke(delegate
{
MessageBox.Show(Post_Result);
response.Close();
});
}), filename);
Thread.Sleep(1000);
}
else
{
MessageBox.Show("User not signed in! Please login to continue...", "Invalid Authentication", MessageBoxButton.OK);
}
}
public class DataContractMultiPartSerializer
{
private string boundary;
public DataContractMultiPartSerializer(string boundary)
{
this.boundary = boundary;
}
private void WriteEntry(StreamWriter writer, string key, object value, string filename)
{
if (value != null)
{
writer.Write("--");
writer.WriteLine(boundary);
if (value is IsolatedStorageFileStream)
{
IsolatedStorageFileStream f = value as IsolatedStorageFileStream;
writer.WriteLine(#"Content-Disposition: form-data; name=""{0}""; filename=""{1}""", key, filename);
writer.WriteLine("Content-Type: image/jpeg");
writer.WriteLine("Content-Length: " + f.Length);
writer.WriteLine();
writer.Flush();
Stream output = writer.BaseStream;
Stream input = f;
byte[] buffer = new byte[4096];
for (int size = input.Read(buffer, 0, buffer.Length); size > 0; size = input.Read(buffer, 0, buffer.Length))
{
output.Write(buffer, 0, size);
}
output.Flush();
writer.WriteLine();
}
else
{
writer.WriteLine(#"Content-Disposition: form-data; name=""{0}""", key);
writer.WriteLine();
writer.WriteLine(value.ToString());
}
}
}
public void WriteObject(Stream stream, object data, string filename)
{
StreamWriter writer = new StreamWriter(stream);
if (data != null)
{
if (data is Dictionary<string, object>)
{
foreach (var entry in data as Dictionary<string, object>)
{
WriteEntry(writer, entry.Key, entry.Value, filename);
}
}
else
{
foreach (var prop in data.GetType().GetFields())
{
foreach (var attribute in prop.GetCustomAttributes(true))
{
if (attribute is System.Runtime.Serialization.DataMemberAttribute)
{
DataMemberAttribute member = attribute as DataMemberAttribute;
writer.Write("{0}={1}&", member.Name ?? prop.Name, prop.GetValue(data));
}
}
}
}
writer.Flush();
}
}
}
Is there a reason you're using a multipart message?
What is the PostMultiPartAsync method you're using? Presumably it's an extension method from somewhere?
In future, try and provide the smallest, complete, piece of code which demonstrates the issue.
Anyway, sorry it's not a full working example, but here are the steps for one way to do this.
Create request and set a calback for BeginGetRequestStream
var request = (HttpWebRequest)WebRequest.Create(App.Config.ServerUris.Login);
request.Method = "POST";
request.BeginGetRequestStream(ReadCallback, request);
In that callback, get the request stream and write your data to it.
using (var postStream = request.EndGetRequestStream(asynchronousResult))
{
// Serialize image to byte array, or similar (that's what imageBuffer is)
postStream.Write(imageBuffer, 0, imageBuffer.Length);
}
Set a callback for the response from the server.
request.BeginGetResponse(ResponseCallback, request);
Check that everything worked OK on the server
private void ResponseCallback(IAsyncResult asynchronousResult)
{
var request = (HttpWebRequest)asynchronousResult.AsyncState;
using (var resp = (HttpWebResponse)request.EndGetResponse(asynchronousResult))
{
using (var streamResponse = resp.GetResponseStream())
{
using (var streamRead = new StreamReader(streamResponse))
{
string responseString = streamRead.ReadToEnd(); // Assuming that the server will send a text based indication of upload success
// act on the response as appropriate
}
}
}
}
On the server you will need to deserialize the data and turn it back into an image as appropriate.
HTH.

Categories