WebResponse reading with callbacks - asynchronous

I have an app where the user can choose to download all content and this was done with this method on other platforms
resp.GetResponseStream().BeginRead(mBuffer, 0, 1448, new AsyncCallback(EndRead), resp);
but a BeginRead-method is not present in the .NET framework used by UWP applications. I need a way to do this in the same way as the other platforms do it, so I can use the callback-function for progressbar updating.
Any ideas?

I need a way to do this in the same way as the other platforms do it, so I can use the callback-function for progressbar updating.
You can use this way as a workaround:
var request = WebRequest.CreateHttp("http://www.bing.com");
var response = await request.GetResponseAsync();
byte[] buffer = new byte[1024];
var stream = await response.GetResponseStream().ReadAsync(buffer, 0, 1024);
// add callback actions here

Related

GetFileFromApplicationUriAsync, CopyAsync, AsStreamForRead not implemented in Uno Platform. Workarounds?

I tried to use the following methods, but all of them appeared as not implemented in Uno (Android). What can I do?
Is there any Xamarin.Essentials alternative?
Or other NuGet package?
Or should I use native implementations on each platform?
And is it even possible to implement it in Uno directly?
var pdfFile = StorageFile.GetFileFromApplicationUriAsync(..);
pdfFile.CopyAsync(..);
(await pdfFile.OpenReadAsync()).AsStreamForRead(); // AsStreamForRead() not implemented
I'm using v1.45.0 of Uno.UI.
As David Oliver pointed out in his answer,
Uno hasn't implemented most of the Windows.StorageFile APIs, as for
the most part there are alternatives available in System.IO, which
will work cross-platform.
So...
To open file from the app package we can set its build action to Embedded Resource instead of Content. And instead of StorageFile.GetFileFromApplicationUriAsync() method we can use this code:
public Stream GetStreamFromResourceFile(string filename, Type callingType = null)
{
var assembly = (callingType ?? GetType()).Assembly;
string foundResourceName = assembly.GetManifestResourceNames().FirstOrDefault(r => r.EndsWith(filename, StringComparison.InvariantCultureIgnoreCase));
if (foundResourceName == null)
throw new FileNotFoundException("File was not found in application resources. Ensure that the filename is correct and its build action is set to 'Embedded Resource'.", filename);
return assembly.GetManifestResourceStream(foundResourceName);
}
to copy a file
await pdfFile.CopyAsync(..);
we change to:
await pdfFile.CopyToAsync(newFile);
and to get a stream for read
(await pdfFile.OpenReadAsync()).AsStreamForRead();
we use:
File.OpenRead(pdfFile);
So in the end we have:
string filename = "File.pdf";
var pdfFile = GetStreamFromResourceFile(filename, GetType());
string newFilePath = Path.Combine(ApplicationData.Current.LocalFolder.Path, filename);
using (var newFile = File.Create(newFilePath))
{
await pdfFile.CopyToAsync(newFile);
}
var fileStream = File.OpenRead(newFilePath);
Uno hasn't implemented most of the Windows.StorageFile APIs, as for the most part there are alternatives available in System.IO, which will work cross-platform.
If you're trying to display a pdf, however, there's no cross-platform option currently. On Android the best way to display a pdf is to launch an intent, on iOS it's possible to display the pdf in a WebView.
Partial example code for Android:
public async Task Read(CancellationToken ct, string filePath)
{
var intent = new Intent(Intent.ActionView);
var file = new Java.IO.File(filePath);
var contentUri = Android.Support.V4.Content.FileProvider.GetUriForFile(ContextHelper.Current, _fileProviderAuthority, file);
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
intent.SetDataAndType(contentUri, "application/pdf");
StartActivity(intent);
}
Partial example code for iOS:
<ios:WebView
Source="{Binding FilePath}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />

Unsupported Media Types when POST to web api

Here is the client :
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost/MP.Business.Implementation.FaceAPI/");
client.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
using (var request = new HttpRequestMessage(HttpMethod.Post, client.BaseAddress + "api/Recognition/Recognize"))
{
request.Content = new ByteArrayContent(pic);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
await client.PostAsync(request.RequestUri, request.Content);
}
}
and the server :
[System.Web.Http.HttpPost]
public string Recognize(byte[] img)
{
//do someth with the byte []
}
I am getting error:
415 Unsupported Media Type
all the time - The request entity's media type 'application/octet-stream' is not supported for this resource. What can i do about it? I've found some answered threads here , but it didnt help.
While byte[] would be a great way to represent application/octet-stream data, this is not the case by default in Web API.
My workaround is in ASP.NET Core 1.1 - the details may be different in other variants.
In your controller method, remove the img parameter. Instead, refer to the Request.Body, which is a Stream. e.g. to save to a file:
using (var stream = new FileStream(someLocalPath, FileMode.Create))
{
Request.Body.CopyTo(stream);
}
The situation is similar for returning binary data from a GET controller method. If you make the return type byte[] then it is formatted with base64! This makes it significantly larger. Modern browsers are perfectly capable of handling raw binary data so this is no longer a sensible default.
Fortunately there is a Response.Body https://github.com/danielearwicker/ByteArrayFormatters:
Response.ContentType = "application/octet-stream";
Response.Body.Write(myArray, 0, myArray.Length);
Make the return type of your controller method void.
UPDATE
I've created a nuget package that enables direct use of byte[] in controller methods. See: https://github.com/danielearwicker/ByteArrayFormatters

How to properly sync Xamarin iOS Sqlite using Azure Mobile Services

In my Xamarin iOS PCL app I'm trying to insert a record into my local Sqlite table, have it synced via Azure Mobile Services, and then read it back.
Here is the code:
private IMobileServiceSyncTable<Job> jobTable;
public async Task InitializeAsync()
{
var store = new MobileServiceSQLiteStore("localdata.db");
store.DefineTable<Job> ();
await this.MobileService.SyncContext.InitializeAsync(store);
jobTable = this.MobileService.GetSyncTable<Job>();
jobTable = this.MobileService.GetSyncTable<Job>();
JObject newJob = new JObject ();
newJob.Add ("Id","job_123");
jobTable.InsertAsync (newJob);
this.MobileService.SyncContext.PushAsync();
var readResult = jobTable.ReadAsync ().Result.AsQueryable();
var resultList = from data in readResult
select data;
var resultCount = resultList.Count ();
}
So far - nothing gets synced up with my Sql Server db (which is on the recieving end of the Mobile Services), and the resultCount remain at 0
I'm sure I do something wrong here, just can't seem to nail what exactly.
-Eugene
You should use PullAsync instead of ReadAsync. Also, you need to await the call to all of your async method calls, such as InsertAsync, PushAsync, and PullAsync.
See this tutorial for a detailed example: http://azure.microsoft.com/en-us/documentation/articles/mobile-services-xamarin-ios-get-started-offline-data/

Async reading chunked content with HttpClient from ASP.NET WebApi

I would like to use HttpClient to read the chunked (in the sense of HTTP 1.1 chunked transfer encoding) content asynchronously.
I am looking at HttpContent async methods at:
MSDN link
However, in the case of returned Task for byte array, for example:
The returned Task object will complete after all of the content has been written as a byte array
I am getting chunked content precisely because server doesn't know ahead of time when will all of the data be available, thus I don't know when will all of the content arrive. Rather than waiting, possibly for hours, for the task to complete, I would like to be able to read the chunks as they arrive.
Can I somehow read part of the response content, like have some task that would complete when every 4K bytes of content are received in response?
Is using HttpClient advantageous at all in this case?
Using HttpClient.SendAsync you can pass a HttpCompletionOption parameter to tell HttpClient not to buffer the response for you and return as soon as it gets the headers. Then you can use ReadAsStreamAsync to get a stream that will allow you to pull the data as it arrives.
Here is a complete example of how to download a file without reading its content to memory with an explanation. Works beautifully.
static async Task HttpGetForLargeFileInRightWay()
{
using (HttpClient client = new HttpClient())
{
const string url =
"https://github.com/tugberkugurlu/ASPNETWebAPISamples/archive/master.zip";
using (HttpResponseMessage response = await client.GetAsync(url,
HttpCompletionOption.ResponseHeadersRead))
using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
{
string fileToWriteTo = Path.GetTempFileName();
using (Stream streamToWriteTo = File.Open(fileToWriteTo, FileMode.Create))
{
await streamToReadFrom.CopyToAsync(streamToWriteTo);
}
}
}
}
Or instead of using CopyToAsync() you can read stream by using StreamReader
using (var stream = await response.Content.ReadAsStreamAsync())
using (var reader = new StreamReader(stream))
{
int bytesCount = 100;
var buffer = new char[bytesCount];
reader.ReadBlock(buffer, 0, bytesCount);
}

How do I make a simple post to Twitter via ASP.NET (VB, preferably)?

I don't want to do anything fancy on Twitter except post to it via my site once a day. I have searched around a bit and there are all sorts of super-complex ways to do every little thing that Twitter does, but there seems to be little documentation on how to do the simplest thing, which is make a post!
Does anyone know how to do this? Or can you at least point me in the right direction? I don't need full wrappers or anything (http://apiwiki.twitter.com/Libraries#C/NET), just one simple function that will post to Twitter.
Thanks!
This is the easiest implementation ever. Up and running in under 2 minutes: Twitterizer
Its fairly simple; you just need to post an xml file to a web page using webrequest.create. This example is close (assumes you have the xml for the message in another place and just pass it into twitterxml variable as a string. The url might not be the right one; found it on this [page][1] which defines the interface
WebRequest req = null;
WebResponse rsp = null;
try
{
string twitterXML = "xml as string";
string uri = "http://twitter.com/statuses/update.format";
req = WebRequest.Create(uri);
//req.Proxy = WebProxy.GetDefaultProxy(); // Enable if using proxy
req.Method = "POST"; // Post method
req.ContentType = "text/xml"; // content type
// Wrap the request stream with a text-based writer
StreamWriter writer = new StreamWriter(req.GetRequestStream());
// Write the XML text into the stream
writer.WriteLine(twitterXML);
writer.Close();
// Send the data to the webserver
rsp = req.GetResponse();
}
[1]: http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-statuses update
There are a couple different ways of doing this, they vary depending on the tools you want to use and have access to. Option 1 will work right out of the box, but the coding can be complicated. Option 3 you will have to download tools for, but once there installed and loaded you should be able to consume the twitter api very quickly.
Use WebRequest.Create to create/send messages to remote endpoints
Use WCF, create a mirror endpoint and access the twitter api using client only endpoint.
Use the WCF REST Starter Kit Preview 2, which has a new class called the HttpClient. I would have to recommend this technique if you can. Here is a great video Consuming a REST Twitter Feed in under 3 minutes.
Here is a sample of using the WCF REST Starter Kit's HttpClient:
public void CreateFriendship(string friend)
{
using (var client = new HttpClient())
{
var url = string.Format("http://www.twitter.com/friendships/create/{0}.xml?follow=true", friend);
client.Post(url)
.CheckForTwitterError()
.EnsureStatusIs(HttpStatusCode.OK);
}
}
Add a comment if you'd like more info about a particular method.
Update:
For Option #1 see this question: Remote HTTP Post with C#
There are a few ways of doing this, you can check out http://restfor.me/twitter and it will give you the code from RESTful documentation.
Essentially making any authenticated call you can follow this logic:
///
/// Executes an HTTP POST command and retrives the information.
/// This function will automatically include a "source" parameter if the "Source" property is set.
///
/// The URL to perform the POST operation
/// The username to use with the request
/// The password to use with the request
/// The data to post
/// The response of the request, or null if we got 404 or nothing.
protected string ExecutePostCommand(string url, string userName, string password, string data) {
WebRequest request = WebRequest.Create(url);
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password)) {
request.Credentials = new NetworkCredential(userName, password);
byte[] bytes = Encoding.UTF8.GetBytes(data);
request.ContentLength = bytes.Length;
using (Stream requestStream = request.GetRequestStream()) {
requestStream.Write(bytes, 0, bytes.Length);
using (WebResponse response = request.GetResponse()) {
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
return reader.ReadToEnd();
}
}
}
}
return null;
}

Resources