Best practice to send a lot of files (images) from your device to your server - asp.net-web-api

I have a list of files to send from my device to my server.
List<myFiles> list = new List<myFiles>() { "[long list of files...]" };
For that I want to create a list of tasks: for each file I should invoke a function that sends to my webapi that file via PUT
public async Task<bool> UploadPhoto(byte[] photoBytes, int PropertyId, string fileName)
{
bool rtn = false;
if (CrossConnectivity.Current.IsConnected)
{
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(photoBytes);
fileContent.Headers.ContentType =
MediaTypeHeaderValue.Parse("multipart/form-data");
fileContent.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment")
{
FileName = fileName + ".jpg"
};
content.Add(fileContent);
string url = RestURL() + "InventoriesPicture/Put";
try
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("authenticationToken", SyncData.Token);
HttpResponseMessage response = await client.PutAsync(url, content);
if (response.IsSuccessStatusCode)
{
rtn = true;
Debug.WriteLine($"UploadPhoto response {response.ReasonPhrase}");
}
else
{
Debug.WriteLine($"UploadPhoto response {response.ReasonPhrase}");
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"UploadPhoto exception {ex.Message}");
}
Debug.WriteLine($"UploadPhoto ends {fileName}");
}
return rtn;
}
In a function I have a foreach that calls UploadPhoto. I think there are too many tasks at the same time then I want to send a file, wait the result from the webapi and then send next file and so on.
What can I do? What is the best practice for that? Or in any case how can I resolve my problem? :)
Thank you in advance

Related

How check byte count (size) of existing blob (file) on Azure

I cannot find a decent example of how to use the latest version of Azure BlobClient to get the byte count of an existing blob.
Here is the code I am working with so far. I cannot figure out how to filter the blob I need to find. I can get them all, but it takes ages.
protected BlobContainerClient AzureBlobContainer
{
get
{
if (!isConfigurationLoaded) { throw new Exception("AzureCloud currently has no configuration loaded"); }
if (_azureBlobContainer == null)
{
if (!string.IsNullOrEmpty(_configuration.StorageEndpointConnection))
{
BlobServiceClient blobClient = new BlobServiceClient(_configuration.StorageEndpointConnection);
BlobContainerClient container = blobClient.GetBlobContainerClient(_configuration.StorageContainer);
container.CreateIfNotExists();
_azureBlobContainer = container;
}
}
return _azureBlobContainer;
}
}
public async Task<Response<BlobProperties>> GetAzureFileSize(string fileName)
{
BlobClient cloudFile = AzureBlobContainer.GetBlobClient(fileName);
await foreach (BlobItem blobItem in AzureBlobContainer.GetBlobsAsync())
{
Console.WriteLine("\t" + blobItem.Name);
}
// Am i supposed to just iterate every single blob on there? how do I filter?
return blobProps;
}
Thoughts?
Ended up doing it like this... I get that it isn't the proper way, but it works.
public async Task<(BlobClient cloudFile, CopyFromUriOperation result)> MigrateFileToAzureBlobAsync(Uri url, string fileName, long? bytesCount)
{
BlobClient cloudFile = AzureBlobContainer.GetBlobClient(fileName);
cloudFile.DeleteIfExists();
BlobCopyFromUriOptions options = new BlobCopyFromUriOptions();
options.Metadata = new Dictionary<string, string>();
options.Metadata.Add("confexbytecount", bytesCount.ToString());
CopyFromUriOperation result = await cloudFile.StartCopyFromUriAsync(url, options);
Response x = await result.UpdateStatusAsync();
await result.WaitForCompletionAsync();
return (cloudFile, result);
}
public async Task<long> GetAzureFileSize(string fileName)
{
long retVal = 0;
await foreach (BlobItem blobItem in AzureBlobContainer.GetBlobsAsync(BlobTraits.All, BlobStates.All, fileName))
{
if (blobItem.Metadata.TryGetValue("confexbytecount", out string confexByteCount))
{
long.TryParse(confexByteCount, out retVal);
}
}
return retVal;
}

ASP.NET Core: Fetch image from external site and save on Server

I want to fetch an image from an external site and save it on the web applicationĀ“s server.
My current code downloads the file, but it won't open because of wrong format, it says.
private Uri _uri;
private HttpClient _client;
[HttpPost]
public async Task WmsExport(ExportImagePostData postData)
{
try
{
PrepareUri(postData);
ValidateUrl(postData);
PrepareRequest(postData);
await FetchImageAndSave_async(postData);
}
catch (TaskCanceledException)
{
HttpContext.Response.StatusCode = 408;
}
catch (Exception ex)
{
var statusCode = 500;
HttpContext.Response.StatusCode = statusCode;
_logger.LogError(ex, ex.Message);
}
}
private void PrepareRequest(ExportImagePostData postData)
{
_client = _customClientFactory.CreateHttpClient();
_client.BaseAddress = _uri;
_client.CopyRequestHeaders(HttpContext);
_client.DefaultRequestHeaders.Add("Accept", "image/png");
}
public async Task FetchImageAndSave_async(ExportImagePostData postData)
{
using (var contentStream = await _client.GetStreamAsync(_client.BaseAddress.OriginalString))
{
await SaveImageOnServer_async(postData, contentStream);
}
}
private async Task SaveImageOnServer_async(ExportImagePostData postData, Stream downloadStream)
{
var filename = "wmsexp" + DateTime.Now.ToString("HHmm") + "." + postData.ImageType;
var directory = "wwwroot/Images/uploads/";
var path = Path.Combine(Directory.GetCurrentDirectory(), directory, filename);
using (var outStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1048576, true))
{
await downloadStream.CopyToAsync(outStream);
}
}
Here is how the images look like in my folder, when I try to open them:
****SOLVED** **
I got help from Roman Marusyk, here on Stack Overflow. Thanks Roman! If you write an answer I be happy to set it as the answer!
The issue seem to have been that I added wrong HTTP-headers, and that some of my paths to the images where incorrect.

TaskContinuation.cs not found exception while accessing WebAPI Task

I'm trying to fetch records from a db cursor from the Client app.
Debugging Web API shows that the Cursor returns records but when returning to the Client it throws mscorlib.pdb not loaded window and clicking on Load option it throws TaskContinuation.cs not found exception
Code snippets as below ( removed irrelevant codes for readability )
WebAPI
[HttpPost("{values}")]
public async Task<ActionResult> Post([FromBody] JToken values)
{
// code removed for readility
string[] cursors = { };
cursors = await cursor.GetRpts();
CursorClass firstCursor = JsonConvert.DeserializeObject<CursorClass>(cursors[0]);
return new OkObjectResult(cursors);
}
public async Task<string[]> GetRpts()
{
try
{
DataTable[] dataTables;
CursorClass[] cursorClasses = new CursorClass[5];
//stripped some code
using (DataAccess dataAccess = new DataAccess()
{
ParamData = PrepareDoc(),
ProcedureName = Constants.Rpt,
RecordSets = this.CursorNumbers,
})
{
Int32 errorNumber = await dataAccess.RunComAsync();
dataTables = dataAccess.TableData;
};
//fetching code stripped off
string[] _cursors = Array.ConvertAll(cursorClasses, JsonConvert.SerializeObject);
return _cursors;
}
catch (Exception ex)
{
string tt = ex.Message;
}
}
public async Task<Int32> RunComAsync()
{
Int32 returnValue = 0;
try
{
//open db connection
//---------- Running the Command in asysnc mode ----------
Task<int> task = new Task<int>(oracleCommand.ExecuteNonQuery);
task.Start();
returnValue = await task;
//--------------------------------------------------------
OracleRefCursor[] refCursor = { null, null, null, null, null };
for (int _sub = 0; _sub < RecordSets; _sub++)
{
//DT declaration / connection code removed
dataAdapter.Fill(dataTable, refCursor[_sub]);
TableData[_sub] = dataTable;
}
}
catch (Exception ex)
{
return LogMsg(ex);
}
finally
{
this.Dispose(true);
}
CloseConnection();
return LogMsg(null,"Successful Operation");
}
Client
private async Task<HttpStatusCode> CallService()
{
HttpResponseMessage _response = null;
try
{
using (HttpRequestMessage requestMessage = new HttpRequestMessage()
{
Content = new System.Net.Http.StringContent(JsonRepo, System.Text.Encoding.UTF8, HeaderJson),
RequestUri = new Uri(UriString),
Method = HttpMethod.Post,
})
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Utils.TOKEN) ;
_response = await httpClient.SendAsync(requestMessage);
if (_response.IsSuccessStatusCode)
{
string httpResponse = await _response.Content.ReadAsStringAsync();
httpString = JsonConvert.DeserializeObject<string[]>(httpResponse);
}
}
}
return ErrorCode;
}
Is that something related to async operation? while debugging the API it confirms the Datatable with records . Any inputs are deeply appreciated.
error image
TIA

Serve large file async and then delete it

Using Web API 2, I have a process that generates a temporary file for the purpose of writing it to the output stream for client consumption. The process can be somewhat long running, taking a few minutes to complete.
What I'd like to do is serve the file asynchronously and then delete it upon completion or cancellation (connection timeout).
Would something like this be close? I haven't tested yet, but I'm wondering of the continue will delete the file before it's served entirely.
public class FileCreationResult
{
public String FilePathOut { get; set; }
}
public class MyFileCreationProcess{
const string NaiveDefaultTempFilePath = #"c:\temp\mytempfile.bin";
public FileCreationResult Execute(){
// do stuff that makes a file
File.WriteAllBytes(NaiveDefaultTempFilePath, NaiveDefaultTempFilePath);
return new FileCreationResult{
FilePathOut = NaiveDefaultTempFilePath
};
}
}
public class Controller : ApiController
{
private readonly MyFileCreationProcess process;
public Controller(MyFileCreationProcess process)
{
this.process = process;
}
public async Task<HttpResponse> GetFile(CancellationToken cancellationToken){
return await Task.Run(()=>{
var fileCreationResult = process.Execute();
using( var stream = File.OpenRead(fileCreationResult.FilePathOut)){
var response = Request.CreateResponse();
response.Content = new StreamContent( stream);
return response;
//
// I would like to delete the file at path fileCreationResult.FilePathOut
// after it has been written to the output stream..
// how would I do this ?
}
});
}
}
You can probably just get away with a try finally block
public async Task<HttpResponse> GetFile(CancellationToken cancellationToken){
return await Task.Run(()=>{
var fileCreationResult = process.Execute();
try{
using( var stream = File.OpenRead(fileCreationResult.FilePathOut)){
var response = Request.CreateResponse();
response.Content = new StreamContent( stream);
return response;
}
}finally{
if(File.Exists(fileCreationResult.FilePathOut))
{
File.Delete(fileCreationResult.FilePathOut);
}
}
});
}
or if you want to use continueWith:
public Task<HttpResponse> GetFile(CancellationToken cancellationToken){
string path;
return Task.Run(()=>{
var fileCreationResult = process.Execute();
path = fileCreationResult.FilePathOut;
using( var stream = File.OpenRead(path)){
var response = Request.CreateResponse();
response.Content = new StreamContent( stream);
return response;
}
}).ContinueWith(x=>{
if(File.Exists(fileCreationResult.FilePathOut))
{
File.Delete(fileCreationResult.FilePathOut);
}
});
}
I should note I don't have a C# compiler handy so it may not compile but I think you get the point.

Azure Notification Hub and WP8 Intermitant notifications

This is a fairly long piece of code but I am getting nowhere with this and cannot see any issues, although I am new to using notification hubs. I am trying to register for targeted notifications (the logged on user) using the notification hub in Azure. After the registration, a test notification is sent.
The issue I am having is that sometimes the notification is sent to the device, and sometimes it is not. It mostly isn't but occasionally when I step through the code on the server, i will get the notification on the emulator come through. Once when I deployed the app to my phone the notification came though on the emulator! I cannot discover a pattern.
My Controller class looks like this;
private NotificationHelper hub;
public RegisterController()
{
hub = NotificationHelper.Instance;
}
public async Task<RegistrationDescription> Post([FromBody]JObject registrationCall)
{
var obj = await hub.Post(registrationCall);
return obj;
}
And the helper class (which is used elsewhere so is not directly in the controller) looks like this;
public static NotificationHelper Instance = new NotificationHelper();
public NotificationHubClient Hub { get; set; }
// Create the client in the constructor.
public NotificationHelper()
{
var cn = "<my-cn>";
Hub = NotificationHubClient.CreateClientFromConnectionString(cn, "<my-hub>");
}
public async Task<RegistrationDescription> Post([FromBody] JObject registrationCall)
{
// Get the registration info that we need from the request.
var platform = registrationCall["platform"].ToString();
var installationId = registrationCall["instId"].ToString();
var channelUri = registrationCall["channelUri"] != null
? registrationCall["channelUri"].ToString()
: null;
var deviceToken = registrationCall["deviceToken"] != null
? registrationCall["deviceToken"].ToString()
: null;
var userName = HttpContext.Current.User.Identity.Name;
// Get registrations for the current installation ID.
var regsForInstId = await Hub.GetRegistrationsByTagAsync(installationId, 100);
var updated = false;
var firstRegistration = true;
RegistrationDescription registration = null;
// Check for existing registrations.
foreach (var registrationDescription in regsForInstId)
{
if (firstRegistration)
{
// Update the tags.
registrationDescription.Tags = new HashSet<string>() {installationId, userName};
// We need to handle each platform separately.
switch (platform)
{
case "windows":
var winReg = registrationDescription as MpnsRegistrationDescription;
winReg.ChannelUri = new Uri(channelUri);
registration = await Hub.UpdateRegistrationAsync(winReg);
break;
case "ios":
var iosReg = registrationDescription as AppleRegistrationDescription;
iosReg.DeviceToken = deviceToken;
registration = await Hub.UpdateRegistrationAsync(iosReg);
break;
}
updated = true;
firstRegistration = false;
}
else
{
// We shouldn't have any extra registrations; delete if we do.
await Hub.DeleteRegistrationAsync(registrationDescription);
}
}
// Create a new registration.
if (!updated)
{
switch (platform)
{
case "windows":
registration = await Hub.CreateMpnsNativeRegistrationAsync(channelUri,
new string[] {installationId, userName});
break;
case "ios":
registration = await Hub.CreateAppleNativeRegistrationAsync(deviceToken,
new string[] {installationId, userName});
break;
}
}
// Send out a test notification.
await SendNotification(string.Format("Test notification for {0}", userName), userName);
return registration;
And finally, my SendNotification method is here;
internal async Task SendNotification(string notificationText, string tag)
{
try
{
var toast = PrepareToastPayload("<my-hub>", notificationText);
// Send a notification to the logged-in user on both platforms.
await NotificationHelper.Instance.Hub.SendMpnsNativeNotificationAsync(toast, tag);
//await hubClient.SendAppleNativeNotificationAsync(alert, tag);
}
catch (ArgumentException ex)
{
// This is expected when an APNS registration doesn't exist.
Console.WriteLine(ex.Message);
}
}
I suspect the issue is in my phone client code, which is here and SubscribeToService is called immediately after WebAPI login;
public void SubscribeToService()
{
_channel = HttpNotificationChannel.Find("mychannel");
if (_channel == null)
{
_channel = new HttpNotificationChannel("mychannel");
_channel.Open();
_channel.BindToShellToast();
}
_channel.ChannelUriUpdated += async (o, args) =>
{
var hub = new NotificationHub("<my-hub>", "<my-cn>");
await hub.RegisterNativeAsync(args.ChannelUri.ToString());
await RegisterForMessageNotificationsAsync();
};
}
public async Task RegisterForMessageNotificationsAsync()
{
using (var client = GetNewHttpClient(true))
{
// Get the info that we need to request registration.
var installationId = LocalStorageManager.GetInstallationId(); // a new Guid
var registration = new Dictionary<string, string>()
{
{"platform", "windows"},
{"instId", installationId},
{"channelUri", _channel.ChannelUri.ToString()}
};
var request = new HttpRequestMessage(HttpMethod.Post, new Uri(ApiUrl + "api/Register/RegisterForNotifications"));
request.Content = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json");
string message;
try
{
HttpResponseMessage response = await client.SendAsync(request);
message = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
message = ex.Message;
}
_registrationId = message;
}
}
Any help would be greatly appriciated as I have been stuck on this now for days! I know this is a lot of code to paste up here but it is all relevant.
Thanks,
EDIT: The SubscribeToService() method is called when the user logs in and authenticates with the WebAPI. The method is here;
public async Task<User> SendSubmitLogonAsync(LogonObject lo)
{
_logonObject = lo;
using (var client = GetNewHttpClient(false))
{
var logonString = String.Format("grant_type=password&username={0}&password={1}", lo.username, lo.password);
var sc = new StringContent(logonString, Encoding.UTF8);
var response = await client.PostAsync("Token", sc);
if (response.IsSuccessStatusCode)
{
_logonResponse = await response.Content.ReadAsAsync<TokenResponseModel>();
var userInfo = await GetUserInfoAsync();
if (_channel == null)
SubscribeToService();
else
await RegisterForMessageNotificationsAsync();
return userInfo;
}
// ...
}
}
I have solved the issue. There are tons of fairly poorly organised howto's for azure notification hubs and only one of them has this note toward the bottom;
NOTE:
You will not receive the notification when you are still in the app.
To receive a toast notification while the app is active, you must
handle the ShellToastNotificationReceived event.
This is why I was experiencing intermittent results, as i assumed you would still get a notification if you were in the app. And this little note is pretty well hidden.
Have you used proper tag / tag expressions while register/send the message. Also, Where are you storing the id back from the notification hub. It should be used when you update the channel uri (it will expire).
I would suggest to start from scratch.
Ref: http://msdn.microsoft.com/en-us/library/dn530749.aspx

Resources