How get upload progress? - c#

Sorry for my english. I have a method in which I send the StorageFile to the server. I tried using Windows.Web.Http.HttpClient, but does not work ( getting an invalid response from the server ) , so I use System.Net.Http.HttpClient.
Here is my code :
public static async void Upload(string uri, StorageFile data, Action<double> progressCallback = null)
{
try
{
byte[] fileBytes =await ReadFile(data);
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
MultipartContent content = new System.Net.Http.MultipartFormDataContent();
var file1 = new ByteArrayContent(fileBytes);
file1.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "file1",
FileName = data.Name,
};
content.Add(file1);
System.Net.Http.HttpResponseMessage response = await client.PostAsync(uri, content);
response.EnsureSuccessStatusCode();
var raw_response = await response.Content.ReadAsByteArrayAsync();
var r2 = Encoding.UTF8.GetString(raw_response, 0, raw_response.Length);
if (r2[0] == '\uFEFF')
{
r2 = r2.Substring(1);
}
Logger.Info(r2);
}
catch (Exception exc)
{
Logger.Error( exc);
}
}
Whether it is possible to change the code to receive progress about downloading a file in callback function?

On Windows Runtime you can try to switch to Windows.Web.HttpClient class. Its PostAsync method returns IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> interface. This interface has Progress event to which you can simply subscribe before awaiting the result.

Simplest way to upload file with progress
I had the same issue, and after some tries found out that you can easily get byte-accurate progress by tracking the Position of the FileStream of the file that you are going to upload.
This is a sample code that shows that.
FileStream fileToUpload = File.OpenRead(#"C:\test.mp3");
HttpContent content = new StreamContent(fileToUpload);
HttpRequestMessage msg = new HttpRequestMessage{
Content=content,
RequestUri = new Uri(--yourUploadURL--)
}
bool keepTracking = true; //to start and stop the tracking thread
new Task(new Action(() => { progressTracker(fileToUpload, ref keepTracking); })).Start();
var result = httpClient.SendAsync(msg).Result;
keepTracking = false; //stops the tracking thread
And define progressTracker() as
void progressTracker(FileStream streamToTrack, ref bool keepTracking)
{
int prevPos = -1;
while (keepTracking)
{
int pos = (int)Math.Round(100 * (streamToTrack.Position / (double)streamToTrack.Length));
if (pos != prevPos)
{
Console.WriteLine(pos + "%");
}
prevPos = pos;
Thread.Sleep(100); //only update progress every 100ms
}
}
And this solved my problem.

Related

Web API MultipartAsync Task no result

The goal is to upload a single file to my webserver and then store it to mssql database by using multipartcontent. While the file is uploading, a progress bar should be displayed in the client (WPF application). The following code sample shows only the upload to memorystream (no database connection).
The connection from client to server works, the upload to the MemoryStream at server-side works and receiving receiving the percentage to client side works (section ContinueWith in my sample code). The problem is, the client doesn't receive the final CreateResponse request - like a timeout or lost connection, I am not sure because i doesn't get an error/exception. The client never receives the task final result.
WebApi:
public class AttachmentsController : ApiController
{
[HttpPost]
[Route("api/Attachments/Upload")]
public async Task<HttpResponseMessage> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
try
{
AttachmentUpload _resultAttachmentUpload = null;
var _provider = new InMemoryMultipartFormDataStreamProvider();
await Request.Content.ReadAsMultipartAsync(_provider)
.ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
throw new HttpResponseException(
HttpStatusCode.InternalServerError);
}
return new AttachmentUpload()
{
FileName = _provider.Files[0].Headers.ContentDisposition
.FileName.Trim('\"'),
Size = _provider.Files[0].ReadAsStringAsync().Result
.Length / 1024
};
});
return Request.CreateResponse<AttachmentUpload>(HttpStatusCode.Accepted,
_resultAttachmentUpload);
}
catch (Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
e.ToString());
}
}
}
WPF Client UploadService.cs:
private async Task<Attachment> UploadAttachment(AttachmentUpload uploadData,
string filePath)
{
try
{
var _encoding = Encoding.UTF8;
MultipartFormDataContent _multipartFormDataContent =
new MultipartFormDataContent();
_multipartFormDataContent.Add(new StreamContent(new MemoryStream(
File.ReadAllBytes(filePath))), uploadData.FileName, uploadData.FileName);
_multipartFormDataContent.Add(new StringContent(uploadData.Id.ToString()),
"AttachmentId", "AttachmentId");
_multipartFormDataContent.Add(new StringContent(
uploadData.Upload.ToString(CultureInfo.CurrentCulture)), "AttachmentUpload",
"AttachmentUpload");
_multipartFormDataContent.Add(new StringContent(
uploadData.DocumentId.ToString()), "DocumentId", "DocumentId");
_multipartFormDataContent.Add(new StringContent(
uploadData.User, _encoding), "User", "User");
//ProgressMessageHandler is instantiate in ctor to show progressbar
var _client = new HttpClient(ProgressMessageHandler);
_client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("multipart/form-data"));
_client.Timeout = TimeSpan.FromMinutes(5);
var _requestUri = new Uri(BaseAddress + "api/Attachments/Upload");
var _httpRequestTask = await _client.PostAsync(_requestUri,
_multipartFormDataContent)
.ContinueWith<AttachmentUpload>(request =>
{
var _httpResponse = request.Result;
if (!_httpResponse.IsSuccessStatusCode)
{
throw new Exception();
}
var _response = _httpResponse.Content.ReadAsStringAsync();
AttachmentUpload _upload =
JsonConvert.DeserializeObject<AttachmentUpload>(_response.Result);
return _upload;
});
var _resultAttachment = _httpRequestTask;
return new Attachment()
{
Id = _resultAttachment.Id,
FileName = _resultAttachment.FileName,
Comment = _resultAttachment.Comment,
Upload = _resultAttachment.Upload,
};
}
catch (Exception e)
{
//Handle exceptions
//file not found, access denied, no internet connection etc etc
var tmp = e;
throw e;
}
}
The program lacks at var _httpRequestTask = await _client.PostAsync(...).
The debugger never reaches the line var _resultAttachment = _httpRequestTask;.
Thank you very much for your help.
First of all, don't mix await and ContinueWith, the introduction of async / await effectively renders ContinueWith obsolete.
The reason that var _resultAttachment = _httpRequestTask; is never hit is because you have created a deadlock.
WPF has a synchronisation context, that ensures continuations resume on the UI thread.
In the line AttachmentUpload _upload = JsonConvert.DeserializeObject<AttachmentUpload>(_response.Result);, _response.Result is a blocking call; it blocks the current thread, until the Task referenced by _response has completed.
The ReadAsStringAsync method, which generated the Task, will attempt to resume once the asynchronous work has completed, and the WPF synchronisation context will force it to use the UI thread, which has been blocked by _response.Result, hence the deadlock.
To remedy this, use the await keyword for every asynchronous call:
var _httpResponse = await _client.PostAsync(_requestUri, _multipartFormDataContent);
if (!_httpResponse.IsSuccessStatusCode)
{
throw new Exception();
}
var _response = await _httpResponse.Content.ReadAsStringAsync();
var _resultAttachment = JsonConvert.DeserializeObject<AttachmentUpload>(_response);
You should also notice that the code is much more readable without the ContinueWith.

PostAsync after execution immediately returns from where inherited

I'm trying to add a face to the faceset in Face++ API. But when I try to make an HTTP post request, it fails on PostAsync method. It just immediately returns to where it was inherited from. As in my example, after executing var response = await client.PostAsync(addUrl, formData), it immediately returns to the Main method, giving me no response at all. Tried to .ConfigureAwait(false), but it didn't help. Maybe you have some ideas why this is happening?
Documentation of the API: https://console.faceplusplus.com/documents/40622182
API:
public async Task<string> AddFace(string faceToken)
{
try
{
HttpContent keyContent = new StringContent(Keys.apiKey);
HttpContent secretContent = new StringContent(Keys.apiSecret);
HttpContent facesetTokenContent = new StringContent(facesetToken);
HttpContent faceTokenContent = new StringContent(faceToken);
using (var formData = new MultipartFormDataContent())
{
formData.Add(keyContent, "api_key");
formData.Add(secretContent, "api_secret");
formData.Add(facesetTokenContent, "faceset_token");
formData.Add(faceTokenContent, "face_tokens");
var response = await client.PostAsync(addUrl, formData).ConfigureAwait(false);
string responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
return responseString;
}
}
catch (Exception e)
{
Console.WriteLine(e);
return null;
}
}
MAIN:
static void Main()
{
Bitmap b = new Bitmap(#"C:\dev\ProtingaVizija\SmartVision\d.jpg");
FaceRecognition fr = new FaceRecognition();
var face = JsonConvert.DeserializeObject<AnalyzedFaces>(fr.AnalyzeImage(b));
string faceToken = face.faces[0].face_token;
Faceset fs = new Faceset("d8d6679e5b37c2612e420f94f0225159");
fs.AddFace(faceToken);
string x = fs.GetDetail().Result;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormFaceDetection());
}
I've just edited the Main and it works!
string res = fs.AddFace(faceToken).Result;

Downloading files not affecting UI

We are currently downloading files using HttpClient because our backend requires certificate.
I have a control - FileRowwhich is an UI element with some code-behind methods for file downloading, like this one:
if (FileIsDownloaded == false)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low, () =>
{
DataManager.Instance.DownloadFile(this);
});
}
if (ThumbnailIsDownloaded == false)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low, () =>
{
DataManager.Instance.DownloadThumbnail(this);
});
}
Downloading single item is fine but when i click download all ( about 50 items ) the whole UI starts to freeze.
As you can see, i have tried to give requests low priority - but still same result.
Answers to common questions:
1) Yes the files should be downloadable all at one time, not one after another.
2) DataManager.Instance.DownloadThumbnail(this) - i do this to give refference to current control so that i could report a progress in a progress bar.
Any suggestions?
EDIT:
Downloading looks like this:
public async void DownloadFile(FileRow fileRow)
{
//Lot of checking for if file exist, if version is the same
string LocalFilename = await DownloadManager.DownloadFile(fileRow.MyFile.file.id, fileRow.MyFile.file.version, fileRow.MyFile.file.filename,fileRow);
// next is just using the filename string
}
And finally my download:
public static async Task<string> DownloadFileOfCustomerAssetRow(int? id, int? version, string filename, FileRow fileRow)
{
try
{
HttpClientHandler aHandler = new HttpClientHandler();
aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
HttpClient aClient = new HttpClient(aHandler);
customerAssetRow.CurrentFileDownload = aClient;
aClient.DefaultRequestHeaders.ExpectContinue = false;
HttpResponseMessage response = await aClient.GetAsync(WebServices.BackendStartUrl + "getFileData?id=" + id + "&version=" + version, HttpCompletionOption.ResponseHeadersRead);
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, Windows.Storage.CreationCollisionOption.GenerateUniqueName);
fileRow.FileName = file.Name;
using (var fs = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
{
Stream stream = await response.Content.ReadAsStreamAsync();
IInputStream inputStream = stream.AsInputStream();
ulong totalBytesRead = 0;
while (true)
{
IBuffer buffer = new Windows.Storage.Streams.Buffer(1024);
buffer = await inputStream.ReadAsync(
buffer,
buffer.Capacity,
InputStreamOptions.None);
if (buffer.Length == 0)
{
break;
}
totalBytesRead += buffer.Length;
fileRow.Progress.Value = fileRow.Progress.Value + 1024;
await fs.WriteAsync(buffer);
}
inputStream.Dispose();
}
fileRow.Progress.Visibility = Visibility.Collapsed;
return file.Name;
}
catch (Exception e)
{
ErrorReporter.ReportError("Error in DownloadManager.cs in function DownloadFile.", e);
return "";
}
}
It's possible that your async method continuations are interrupting the UI thread too much. Try creating a stronger separation between your background logic (DownloadFileOfCustomerAssetRow) and your UI (FileRow) by introducing an IProgress<T> reporter. Then ensure that every await in your background logic has a ConfigureAwait(false) on it.
public static async Task<string> DownloadFileOfCustomerAssetRow(int? id, int? version, string filename, IProgress<int> progress)
{
HttpClientHandler aHandler = new HttpClientHandler();
aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
HttpClient aClient = new HttpClient(aHandler);
customerAssetRow.CurrentFileDownload = aClient;
aClient.DefaultRequestHeaders.ExpectContinue = false;
HttpResponseMessage response = await aClient.GetAsync(WebServices.BackendStartUrl + "getFileData?id=" + id + "&version=" + version, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, Windows.Storage.CreationCollisionOption.GenerateUniqueName).ConfigureAwait(false);
fileRow.FileName = file.Name;
using (var fs = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite).ConfigureAwait(false))
{
Stream stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
IInputStream inputStream = stream.AsInputStream();
ulong totalBytesRead = 0;
while (true)
{
IBuffer buffer = new Windows.Storage.Streams.Buffer(1024);
buffer = await inputStream.ReadAsync(
buffer,
buffer.Capacity,
InputStreamOptions.None).ConfigureAwait(false);
if (buffer.Length == 0)
{
break;
}
totalBytesRead += buffer.Length;
if (progress != null)
progress.Report(totalBytesRead);
await fs.WriteAsync(buffer).ConfigureAwait(false);
}
inputStream.Dispose();
}
return file.Name;
}

How to Convert Synchronous 4.0 to ASynchronous in C# 4.5

I am having a hard time convert the below code which i have created in 4.0 to 4.5 using HttpClient.
According to my understand i guess if i create multiple web requests in the GUI thread itself without blocking the GUI if i got with asynchronous requeest.
how to convert the below code to Asynchronous using HttpClient in 4.5
// This is what called when button is clicked
Task t3 = new Task(SpawnTask);
t3.Start();
//if noofthreads are less 50 then GUI is woking fine.. if number increases then takes much time for repaint..
//where as other softwares are working without any problem even if the threads are more than 500!! in the same system
public void SpawnTask()
{
try
{
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = noofthreads;
Parallel.ForEach(
urls,
po,
url => checkpl(url));
}
catch (Exception ex)
{
}
}
public void checkpl(string url)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = 60*1000;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string stext = "";
using (BufferedStream buffer = new BufferedStream(response.GetResponseStream()))
{
using (StreamReader reader = new StreamReader(buffer))
{
stext = reader.ReadToEnd();
}
}
response.Close();
if (stext .IndexOf("domainname.com") != -1)
{
tfound = tfound + 1;
string lext = "Total Found : "+tfound.ToString();
label3.BeginInvoke(new InvokeDelegate(UpdateLabel), ltext);
slist.Add(url);
textBox2.BeginInvoke(new InvokeDelegate4(UpdateText), "Working Url " + url);
}
}
catch (Exception ex)
{
}
}
Since you are using .NET 4.5 you can use the new async and await keywords. Here is what it might look like.
private async void YourButton_Click(object sender, EventArgs args)
{
YourButton.Enabled = false;
try
{
var tasks = new List<Task>();
foreach (string url in Urls)
{
tasks.Add(CheckAsync(url));
}
await TaskEx.WhenAll(tasks);
}
finally
{
YourButton.Enabled = true;
}
}
private async Task CheckAsync(string url)
{
bool found = await UrlResponseContainsAsync(url, "domainname.com");
if (found)
{
slist.Add(url);
label3.Text = "Total Found: " + slist.Count.ToString();
textbox2.Text = "Working Url " + url;
}
}
private async Task<bool> UrlResponseContainsAsync(string url, string find)
{
var request = WebRequest.Create(url);
request.Timeout = 60 * 1000;
using (WebResponse response = await request.GetResponseAsync())
{
using (var buffer = new BufferedStream(response.GetResponseStream()))
using (var reader = new StreamReader(buffer))
{
string text = reader.ReadToEnd();
return text.Contains(find);
}
}
}

GetFileFromApplicationUriAsync seem to hang forever

I am trying to read data from a file, and the GetFileFromApplicationUriAsync, most of the times just hangs for ever, any ideas why that happens. I am debugging the app on the simulator.
A couple notes :
BaseLocalFolder = "ms-appdata:///local/";
Uri baseUri = new Uri(BaseLocalFolder);
Uri cardsUri = new Uri(baseUri, Path.Combine(set,"cards.ccc"));
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(cardsUri);
using (IInputStream stream = await file.OpenSequentialReadAsync())
{
using (StreamReader rdr = new StreamReader(stream.AsStreamForRead()))
{
var cardArray = Windows.Data.Json.JsonArray.Parse(rdr.ReadToEnd());
foreach (Tables.Card card in cardArray.ToCards())
{
_cards.Add(card.ToCardViewModel());
}
}
}
UPDATES:
After I changed the code according to suggestions, I started having the same issue with ReadTextAsync(), once it hits that line it stays there forever, I never get the string.
StorageFile file = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFileAsync(Path.Combine(set, "cards.ccc"));
try
{
string allCards = await Windows.Storage.FileIO.ReadTextAsync(file);
var cardArray = Windows.Data.Json.JsonArray.Parse(allCards);
foreach (Tables.Card card in cardArray.ToCards())
{
_cards.Add(card.ToCardViewModel());
}
}
catch (FileNotFoundException err)
{
string s = "";
}

Categories