How to work with large images on Blazor/ Web - html

my question is how to manage really large images on the web. Im using blazor, I need to send and recive Images of 100mb or more, And im using webshockets to send btwin the web and the server that send images. Im using base64 but its also slow.
this is how i send and i recive an image
public class WebshocketConections
{
public Uri _Uri = new Uri("ws://xxx.xxx.xxx.xxx:YYYY");
ClientWebSocket _cliente;
CancellationTokenSource cts = new();
public delegate void CheckNewMessage(string a);
public event CheckNewMessage? onMessage;
public WebshocketConections(ClientWebSocket client, Uri uri)
{
_cliente = client;
_Uri = uri;
}
public async Task Connect()
{
cts.CancelAfter(TimeSpan.FromSeconds(100));
await _cliente.ConnectAsync(_Uri, cts.Token);
await Echo(_cliente);
}
public async Task SendString(string message)
{
ArraySegment<byte> byteToSend = await Task.Run(() => { return new ArraySegment<byte>(Encoding.UTF8.GetBytes(message)); });
await _cliente.SendAsync(byteToSend, WebSocketMessageType.Text, true, cts.Token);
}
public async Task SendImage(byte[] Base64)
{
ArraySegment<byte> byteToSend = await Task.Run(() => { return new ArraySegment<byte>(Encoding.UTF8.GetBytes("img" + Convert.ToBase64String(Base64))); });
await _cliente.SendAsync(byteToSend, WebSocketMessageType.Binary, true, cts.Token);
}
public async Task<ClientWebSocket> GetClient()
{
return _cliente;
}
private async Task Echo(ClientWebSocket client)
{
var buffer = new byte[1024 * 1000000];
var receiveResult = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue)
{
onMessage(Encoding.UTF8.GetString(buffer, 0, receiveResult.Count));
await client.SendAsync(
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None);
receiveResult = await client.ReceiveAsync(
new ArraySegment<byte>(buffer),
CancellationToken.None);
}
await client.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
}

Related

StateHasChanged not updating on Blazor server app

I am working on a Blazor Server app over a SignalR connection with an ASP.NET Core API, this code works fine in WebAssembly but for some reason it doesn't works in Blazor Server app.
I suspect the problem is that StateHasChanged() isn't making effect, since Console.WriteLine($"Company Name: {item.CompanyName}, Volumn: {item.Volume}"); is printing in console but MarketData isn't updating UI at StateHasChanged().
For more context; full code is explained here:
https://www.webnethelper.com/2022/01/aspnet-core-6-signalr-creating-real.html
But I guess it's just a common fix; but I can't find the solution.
I've also tried with InvokeAsync(() => StateHasChanged()); as mentioned here in stackoverflow but It didnt work. Can anyone help me fix this issue?
private HubConnection? hubConn;
private string? ConnectionStatusMessage;
public List<Market> MarketData = new List<Market>();
public List<Market> MarketReceivedData = new List<Market>();
private List<string> xSource;
private List<int> ySource;
private List<object> source;
protected override async Task OnInitializedAsync()
{
xSource = new List<string>();
ySource = new List<int>();
source = new List<object>();
await service.GetMarketEndpoint();
await Start();
}
private async Task Start()
{
hubConn = new HubConnectionBuilder().WithUrl("https://localhost:7193/marketdata").Build();
await hubConn.StartAsync();
if(hubConn.State == HubConnectionState.Connected )
ConnectionStatusMessage = "Connection is established Successfully...";
else
ConnectionStatusMessage = "Connection is not established...";
}
private void MarketDataListener(string chartType)
{
hubConn.On<List<Market>>("SendMarketStatusData", async (data) =>
{
MarketData = new List<Market>();
foreach (var item in data)
{
Console.WriteLine($"Company Name: {item.CompanyName}, Volumn: {item.Volume}");
xSource.Add(item.CompanyName);
ySource.Add(item.Volume);
}
source.Add(ySource);
source.Add(xSource);
MarketData = data;
StateHasChanged();
await js.InvokeAsync<object>(chartType, source.ToArray());
xSource.Clear();
ySource.Clear();
});
}
private void ReceivedMarketDataListener()
{
hubConn.On<List<Market>>("CommunicateMarketData", (data) =>
{
MarketReceivedData = data;
StateHasChanged();
});
}
public async Task Dispose()
{
await hubConn.DisposeAsync();
}
async Task generateLineChartTask()
{
MarketDataListener("marketLineChart");
ReceivedMarketDataListener();
await service.GetMarketDataAsync();
}
async Task generateBarChartTask()
{
MarketDataListener("marketBarChart");
ReceivedMarketDataListener();
await service.GetMarketDataAsync();
}
The main difference here is that Blazor Serverside is multithreaded so the callbacks from the circuit will execute on a different Thread.
StateHasChanged() has to be executed on the main (UI) thread so call it like InvokeAsync(StateHasChanged), which is short for InvokeAsync(() => StateHasChanged()).
And be aware of other threading risks. Ie, don't share data like Lists between threads.

Json from file not loading the first time

I'm trying to get a online JSON, save it locally for offline use. The issue is that when I try to get the data from the saved JSON file, the first time I open the new "window" no data is loading, because my "goggo1" variable does not receive the new value from reading the JSON file. The funny thing is if I go back and the open it again, then "goggo1" is getting the value required to show the JSON contents.
class HomePagerstare extends StatefulWidget {
Contact contact = new Contact();
String title, content;
#override
_HomePageState createState() => _HomePageState();
}
Future<String> get getFilePath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get getFile async {
final path = await getFilePath;
return File('$path/myfile.json');
}
Future<File> saveToFile(String datar) async {
final file = await getFile;
return file.writeAsString(datar);
}
Future<String> readFromFile() async {
try {
final file = await getFile;
final String fileContents = await file.readAsString();
return fileContents;
} catch (e) {
return "aici e eroare";
}
}
class _HomePageState extends State<HomePagerstare> implements HomeContract {
List<Note> data1 = List<Note>();
List<Note> data2 = List<Note>();
var goggo1 = "";
Future<List<Note>> loadJsonData() async {
var data = List<Note>();
this.setState(() {
print(goggo1);
var datas = jsonDecode(goggo1);
for (var noteJson in datas) {
data.add(Note.fromJson(noteJson));
Note note =
Note(noteJson["id"], noteJson["title"], noteJson["content"]);
}
});
return data;
}
#override
void initState() {
print("1Nu Suntem Online");
super.initState();
readFromFile().then((fileContents) {
setState(() {
goggo1 = fileContents;
});
});
print("1Nu Suntem Online");
this.loadJsonData().then((value) {
setState(() {
data1.addAll(value);
data2 = data1;
print("2Nu Suntem Online");
});
});
}
class Note {
String title;
String content;
int id;
the note.dart
Note(this.id, this.title, this.content);
Note.fromJson(Map<String, dynamic> json) {
id = json['id'];
title = json['title'];
content = json['content'];
}
}
Please help!
I think I could give you an idea. Try something like this,
readFromFile().then((fileContents) {
setState(() {
goggo1 = fileContents;
});
this.loadJsonData().then((value) {
setState(() {
data1.addAll(value);
data2 = data1;
print("2Nu Suntem Online");
});
});
});
Hope that works!

UWP app crashes while performing an async method

I have created two methods in my BaseViewModel (MVVMLight App) to perform any async code with some logic:
public async Task PerformOperation(Func<Task> action)
{
IsBusy = true;
try
{
await action?.Invoke();
}
catch(Exception ex)
{
// logging is here
}
finally
{
IsBusy = false;
}
}
public async Task PerformOperation(params Operation[] actions)
{
IsBusy = true;
MultipleOperatrions = true;
OperationStatuses = new ObservableCollection<OperationStatus>();
try
{
foreach(var action in actions)
{
var status = new OperationStatus() { StatusText = action.StatusText };
OperationStatuses.Add(status);
try
{
await action?.AsyncAction();
status.Success = true;
}
catch
{
status.Success = false;
}
finally
{
status.IsFinished = true;
}
}
}
catch (Exception ex)
{
// logging is here
}
finally
{
await Task.Delay(1000);
IsBusy = false;
MultipleOperatrions = false;
OperationStatuses = new ObservableCollection<OperationStatus>();
}
}
My models:
public class Operation
{
public Func<Task> AsyncAction { get; private set; }
public string StatusText { get; private set; }
public Operation(Func<Task> action, string statusText)
{
AsyncAction = action;
StatusText = statusText;
}
}
My code of calling the methods in the view model:
...
private IAsyncCommand _buildCommand;
public IAsyncCommand BuildCommand => _buildCommand ?? (_buildCommand = new AsyncCommand(Build));
#endregion
#region Methods
public async Task Build()
{
// IT WORKS
// this method is used for performing only a single operation
// await PerformOperation(async () => { await Task.Delay(3000); });
// IT CRASHES THE APP
// for many tasks
await PerformOperation(new Operation(async () => { await Task.Delay(3000); }, "Preparing..."));
}
...
So, if I call PerformOperation to call just only a single method - it works fine. But it doesn't for the method of performing of several operations - the application just crashes without any exception or messages. I subscribed in App.cs to UnhandledException event to catch it but the app just crashes.

BackgroundTask in Lifecycle Events works but not automatically

I am trying to update the data in my secondary tile using a background task. It gets updated when I select my background task from the lifecycle events dropdown. However it doesn't work automatically. Does it mean that my SystemTrigger is not getting fired? Please help.
Here is my code:
public static void CheckIfBackgroundTaskExist()
{
if (BackgroundTaskRegistration.AllTasks.Count < 1)
{
RegisterBackgroundTask();
}
}
public static void RegisterBackgroundTask()
{
BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
builder.Name = "SecondaryTileUpdate";
builder.TaskEntryPoint = "BackgroundTaskLiveTile.SecondaryTileUpdater";
//IBackgroundTrigger trigger = new MaintenanceTrigger(15, false);
IBackgroundTrigger trigger = new SystemTrigger(SystemTriggerType.TimeZoneChange, false);
builder.SetTrigger(trigger);
IBackgroundTaskRegistration task = builder.Register();
}
public async void Run(IBackgroundTaskInstance taskInstance)
{
//HERE: request a deferral
var deferral = taskInstance.GetDeferral();
var list = await SecondaryTile.FindAllAsync();
foreach (SecondaryTile liveTile in list)
{
HttpClient client = new HttpClient();
var response = await client.GetAsync(url, HttpCompletionOption.ResponseContentRead);
if (!response.IsSuccessStatusCode)
{
//
}
if (response != null)
{
string content = await response.Content.ReadAsStringAsync();
try
{
MyProperty= JsonConvert.DeserializeObject<MyClass>(content);
}
catch
{
//MessageBox.Show("Unable to retrieve data");
}
}
// Update Secondary Tiles
if (liveTile.TileId == "MySecondaryTileId")
{
await UpdateMyTile();
}
}
}

How cancel Async Call in Windows Phone?

I have a list wich is loaded with elements each time the user make a research...These elements contain an Icon which is dowloaded with an async method GetByteArrayAsync of the HttpClient object. I have an issue when the user make a second research while the icon of the first list are still downloading.Because the list of elements is changing while Icon downloads are processing on each element of the first list. So my guess is that I need to cancel these requests each time the user proceed to a new research...Ive readen some stuuf on Task.run and CancellationTokenSource but I can't find really helpful example for my case so here is my code...Hope you can help me with that ...Thank you
public static async Task<byte[]> DownloadElementFile(BdeskElement bdeskElement)
{
//create and send the request
DataRequest requesteur = new DataRequest();
byte[] encryptedByte = await requesteur.GetBytesAsync(dataRequestParam);
return encryptedByte;
}
public async Task<Byte[]> GetBytesAsync(DataRequestParam datarequesparam)
{
var handler = new HttpClientHandler { Credentials = new NetworkCredential(datarequesparam.AuthentificationLogin, datarequesparam.AuthentificationPassword, "bt0d0000") };
HttpClient httpClient = new HttpClient(handler);
try
{
byte[] BytesReceived = await httpClient.GetByteArrayAsync(datarequesparam.TargetUri);
if (BytesReceived.Length > 0)
{
return BytesReceived;
}
else
{
return null;
}
}
catch (WebException)
{
throw new MyException(MyExceptionsMessages.Webexception);
}
}
EDIT
public async Task<Byte[]> GetBytesAsync(DataRequestParam datarequesparam)
{
var handler = new HttpClientHandler { Credentials = new NetworkCredential(datarequesparam.AuthentificationLogin, datarequesparam.AuthentificationPassword, "bt0d0000") };
HttpClient httpClient = new HttpClient(handler);
try
{
cts = new CancellationTokenSource();
HttpResponseMessage reponse = await httpClient.GetAsync(datarequesparam.TargetUri,cts.Token);
if (reponse.StatusCode == HttpStatusCode.OK)
{
byte[] BytesReceived = reponse.Content.ReadAsByteArrayAsync().Result;
if (BytesReceived.Length > 0)
{
return BytesReceived;
}
else
{
return null;
}
}
else
{
return null;
}
}
catch (WebException)
{
throw new MyException(MyExceptionsMessages.Webexception);
}
catch(OperationCanceledException)
{
throw new OperationCanceledException();
}
EDIT2
I need to cancel this funntion when the user make a new research and the list "listBoxGetDocsLibs" changed.
private async void LoadIconDocLibs()
{
foreach (var doclib in listBoxGetDocsLibs)//ERROR HERE COLLECTION HAS CHANGED
{
doclib.Icon = new BitmapImage();
try
{
byte[] Icon = await ServerFunctions.GetDocLibsIcon(doclib);
if (Icon != null)
{
{
var ms = new MemoryStream(Icon);
BitmapImage photo = new BitmapImage();
photo.DecodePixelHeight = 64;
photo.DecodePixelWidth = 92;
photo.SetSource(ms);
doclib.Icon = photo;
}
}
}
catch(OperationCanceledException)
{
}
}
}
First you need to define CancellationTokenSource:
private System.Threading.CancellationTokenSource cts;
place above code somewhere, where you can access it with your Button or other method.
Unfortunately GetByteArrayAsync lacks Cancelling - so it cannot be used with cts.Token, but maybe you can accomplish your task using GetAsync - which supports Cancelling:
ctsDownload = new System.Threading.CancellationTokenSource();
HttpResponseMessage response = await httpClient.GetAsync(requestUri, cts.Token);
Then you can get your content from response.
And when you want to Cancel your Task it can look like this:
private void cancelBtn_Click(object sender, RoutedEventArgs e)
{
if (this.cts != null)
this.cts.Cancel();
}
When you Cancel task an Exception will be thrown.
If you want to cancel your own async Task, a good example you can find at Stephen Cleary blog.
EDIT - you can also build your own method (for example with HttpWebRequest) which will support Cancelling:
For this purpose you will have to extend HttpWebRequest (under WP it lacks GetResponseAsync):
// create a static class in your namespace
public static class Extensions
{
public static Task<HttpWebResponse> GetResponseAsync(this HttpWebRequest webRequest)
{
TaskCompletionSource<HttpWebResponse> taskComplete = new TaskCompletionSource<HttpWebResponse>();
webRequest.BeginGetResponse(
asyncResponse =>
{
try
{
HttpWebRequest responseRequest = (HttpWebRequest)asyncResponse.AsyncState;
HttpWebResponse someResponse = (HttpWebResponse)responseRequest.EndGetResponse(asyncResponse);
taskComplete.TrySetResult(someResponse);
}
catch (WebException webExc)
{
HttpWebResponse failedResponse = (HttpWebResponse)webExc.Response;
taskComplete.TrySetResult(failedResponse);
}
catch (Exception exc) { taskComplete.SetException(exc); }
}, webRequest);
return taskComplete.Task;
}
}
Then your method can look like this:
public async Task<Byte[]> GetBytesAsync(DataRequestParam datarequesparam, CancellationToken ct)
{
HttpWebRequest request = HttpWebRequest.CreateHttp(datarequesparam.TargetUri);
request.Method = "GET";
request.Credentials = new NetworkCredential(datarequesparam.AuthentificationLogin, datarequesparam.AuthentificationPassword, "bt0d0000");
request.AllowReadStreamBuffering = false;
try
{
if (request != null)
{
using (HttpWebResponse response = await request.GetResponseAsync())
using (Stream mystr = response.GetResponseStream())
using (MemoryStream output = new MemoryStream())
{
const int BUFFER_SIZE = 10 * 1024;
byte[] buf = new byte[BUFFER_SIZE];
int bytesread = 0;
while ((bytesread = await mystr.ReadAsync(buf, 0, BUFFER_SIZE)) > 0)
{
output.Write(buf, 0, bytesread);
ct.ThrowIfCancellationRequested();
}
return output.ToArray();
}
}
else return null;
}
catch (WebException)
{
throw new MyException(MyExceptionsMessages.Webexception);
}
}
You can freely change Buffer Size which will affect how often Cancellation will be checked.
I haven't tried this but I think it should work.