ServiceStack.Api.Swagger SwaggerResourcesService excluding Routes begining with Route Parameter - servicestack

I am using Servicestack version 4.0.52.0 and ServiceStack.Api.Swagger 4.0.0.0
I have defined all routes with variable route parameter in serviceStack application host derived class(AppHostBase):Configure method
public class AppHost : AppHostBase
{
public override void Configure(Funq.Container container)
{
//Infrastructure code goes here
....
//Add routes
Routes.Add<TestRequestDTO>("/{MyApplicationKey}/TestOperation/", "GET")
}
}
Although these routes are visible in ServiceStack metadata page, swagger UI does not show routes begining with MyApplicationKey; Digging deeper into codebase of we found the issue in ServiceStack.Api.Swagger.SwaggerResourcesService.Get method:
[AddHeader(DefaultContentType = MimeTypes.Json)]
[DefaultRequest(typeof(SwaggerResources))]
[Restrict(VisibilityTo = RequestAttributes.None)]
public class SwaggerResourcesService : Service
{
private readonly Regex resourcePathCleanerRegex = new Regex(#"/[^\/\{]*", RegexOptions.Compiled);
internal static Regex resourceFilterRegex;
internal static Action<SwaggerResourcesResponse> ResourcesResponseFilter { get; set; }
internal const string RESOURCE_PATH = "/resource";
public object Get(SwaggerResources request)
{
var basePath = base.Request.GetBaseUrl();
var result = new SwaggerResourcesResponse
{
BasePath = basePath,
Apis = new List<SwaggerResourceRef>(),
ApiVersion = HostContext.Config.ApiVersion,
Info = new SwaggerInfo
{
Title = HostContext.ServiceName,
}
};
var operations = HostContext.Metadata;
var allTypes = operations.GetAllOperationTypes();
var allOperationNames = operations.GetAllOperationNames();
foreach (var operationName in allOperationNames)
{
if (resourceFilterRegex != null && !resourceFilterRegex.IsMatch(operationName)) continue;
var name = operationName;
var operationType = allTypes.FirstOrDefault(x => x.Name == name);
if (operationType == null) continue;
if (operationType == typeof(SwaggerResources) || operationType == typeof(SwaggerResource))
continue;
if (!operations.IsVisible(Request, Format.Json, operationName)) continue;
CreateRestPaths(result.Apis, operationType, operationName);
}
result.Apis = result.Apis.OrderBy(a => a.Path).ToList();
if (ResourcesResponseFilter != null)
ResourcesResponseFilter(result);
return new HttpResult(result) {
ResultScope = () => JsConfig.With(includeNullValues:false)
};
}
protected void CreateRestPaths(List<SwaggerResourceRef> apis, Type operationType, string operationName)
{
var map = HostContext.ServiceController.RestPathMap;
var feature = HostContext.GetPlugin<SwaggerFeature>();
var paths = new List<string>();
foreach (var key in map.Keys)
{
paths.AddRange(map[key].Where(x => x.RequestType == operationType).Select(t => **resourcePathCleanerRegex.Match**(t.Path).Value));
}
......
}
Any hints on how we can fix the issue? Is there any way to tell value of MyApplicationKey to Swagger API when it is doing resource listing?

There is an opportunity to modify the Swagger Response by specifying a custom ResourcesResponseFilter when you register the SwaggerFeature plugin, e.g:
Plugins.Add(new SwaggerFeature {
ResourceFilterPattern = #"/[^\/\{]*",
ResourcesResponseFilter = response => ...
});
Which will let you modify the Swagger Response that the Swagger UI receives.

Related

display data dynamically on a razor page

There's a console app that uses async method for web site scraping.
using Microsoft.AspNetCore.Authentication;
using HtmlAgilityPack;
using System.Net;
class Program
{
static void Main(string[] args)
{
submainAsync();
Console.ReadKey();
}
private static async Task<bool> submainAsync()
{
ADManager adManager = new ADManager();
List<Person> people;
people = adManager.GetPeopleByPath("ls.local");
string cookie = "0123456789";
if (cookie == String.Empty)
{
Console.WriteLine("check your password");
return false;
}
var peopleAS = getPIDsAsync(people, cookie);
int total = people.Count;
await foreach (Person prs in peopleAS)
{
Console.WriteLine("next is {0} countdown {1}", prs.samAccountName, total--);
}
return true;
}
static private async IAsyncEnumerable<Person> getPIDsAsync(List<Person> people, string inCookie)
{
foreach (Person prs in people)
{
var baseAddress = new Uri("http://192.168.1.100");
using (var handler = new HttpClientHandler { UseCookies = false })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
string request = "/doc/admin/directories.asp?act=test&id=META&login=" + prs.samAccountName + "&s=DEFAULT";
var message = new HttpRequestMessage(HttpMethod.Get, request);
message.Headers.Add("Cookie", "t%5FDEFAULT%5Fadmin="+ inCookie);
var result = await client.SendAsync(message);
var resultString = await result.Content.ReadAsStringAsync();
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(resultString);
var node = doc.DocumentNode.SelectSingleNode("//*[#id=\"value\"]");
var val = node.InnerText;
prs.PID = node.InnerText;
}
yield return prs;
}
}
}
[ yield return, await foreach ] writelines out data nicely as soon as the next piece of data is ready. Now, I moved the code from a console app to a razor page app, so submainAsync() is called from OnPost. The question is - how can I display scraped data in a table on a razor page? My goal is to display the data dynamically, as soon as the next piece is ready, just like it happens in the Console.
Would someone please point me in the right direction?

Create user inside a transaction

When creating a user like so:
using var trans = Db.BeginTransaction();
AuthRepository.CreateUserAuth(newUser, request.Password);
AuthRepository.AssignRoles(created, new List<string> { request.role });
//.. do other stuff
throw new Exception("other code may throw this");
trans.Commit();
The Auth repo has it's own connection so it's not part of the transaction. This means, if my code bails out, I end up with an unwanted user.
Is there any way to use a transaction with AuthRepository or is only way to manually write to user and role tables? I couldn't find an example of creating the password hash in docs when manually saving, is there any example?
You can't use the existing Auth Repository APIs within a transaction because each uses its own DB connection.
If you want you can take the implementation of those OrmLite Auth Respository APIs in OrmLiteAuthRepository.cs and move them into your method so they're all using the same DB Connection + Transaction.
Also note that to use Transactions in OrmLite you should use OpenTransaction() instead, e.g:
using (var dbTrans = db.OpenTransaction())
{
}
Adding class I used here in case it helps anyone as needed a little refactoring:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using OutReachPete.ServiceModel.User;
using ServiceStack;
using ServiceStack.Auth;
using ServiceStack.OrmLite;
namespace OutReachPete.ServiceInterface.User
{
public class UserHelper
{
public static bool ForceCaseInsensitiveUserNameSearch { get; set; } = true;
public static bool UseDistinctRoleTables { get; set; } = true;
public static UserAuthCustom CreateUserAuth(UserAuthCustom newUser, string password, IDbConnection db)
{
newUser.ValidateNewUser(password);
AssertNoExistingUser(db, newUser);
newUser.PopulatePasswordHashes(password);
newUser.CreatedDate = DateTime.UtcNow;
newUser.ModifiedDate = newUser.CreatedDate;
db.Save(newUser);
newUser = db.SingleById<UserAuthCustom>(newUser.Id);
return newUser;
}
public static void AssertNoExistingUser(IDbConnection db, IUserAuth newUser,
IUserAuth exceptForExistingUser = null)
{
if (newUser.UserName != null)
{
var existingUser = GetUserAuthByUserName(db, newUser.UserName);
if (existingUser != null
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
throw new ArgumentException(string.Format(ErrorMessages.UserAlreadyExistsTemplate1,
newUser.UserName.SafeInput()));
}
if (newUser.Email != null)
{
var existingUser = GetUserAuthByUserName(db, newUser.Email);
if (existingUser != null
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
throw new ArgumentException(string.Format(ErrorMessages.EmailAlreadyExistsTemplate1,
newUser.Email.SafeInput()));
}
}
public static UserAuthCustom GetUserAuthByUserName(IDbConnection db, string userNameOrEmail)
{
var isEmail = userNameOrEmail.Contains("#");
var lowerUserName = userNameOrEmail.ToLower();
UserAuthCustom userAuth = null;
// Usernames/Emails are saved in Lower Case so we can do an exact search using lowerUserName
if (HostContext.GetPlugin<AuthFeature>()?.SaveUserNamesInLowerCase == true)
{
return isEmail
? db.Select<UserAuthCustom>(q => q.Email == lowerUserName).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName == lowerUserName).FirstOrDefault();
}
// Try an exact search using index first
userAuth = isEmail
? db.Select<UserAuthCustom>(q => q.Email == userNameOrEmail).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName == userNameOrEmail).FirstOrDefault();
if (userAuth != null)
return userAuth;
// Fallback to a non-index search if no exact match is found
if (ForceCaseInsensitiveUserNameSearch)
{
userAuth = isEmail
? db.Select<UserAuthCustom>(q => q.Email.ToLower() == lowerUserName).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName.ToLower() == lowerUserName).FirstOrDefault();
}
return userAuth;
}
public static void AssignRoles(IAuthRepository userAuthRepo, IUserAuth userAuth, IDbConnection db,
ICollection<string> roles = null, ICollection<string> permissions = null)
{
if (userAuthRepo is IManageRoles managesRoles)
{
AssignRoles(userAuth.Id.ToString(), db, roles, permissions);
}
else
{
AssignRolesInternal(userAuth, roles, permissions);
SaveUserAuth(userAuth, db);
}
}
public static IUserAuth GetUserAuth(string userAuthId, IDbConnection db)
{
if (string.IsNullOrEmpty(userAuthId))
throw new ArgumentNullException(nameof(userAuthId));
return db.SingleById<UserAuthCustom>(int.Parse(userAuthId));
}
public static void AssignRoles(string userAuthId, IDbConnection db, ICollection<string> roles = null, ICollection<string> permissions = null)
{
var userAuth = GetUserAuth(userAuthId, db);
if (!UseDistinctRoleTables)
{
if (!roles.IsEmpty())
{
foreach (var missingRole in roles.Where(x => userAuth.Roles == null || !userAuth.Roles.Contains(x)))
{
if (userAuth.Roles == null)
userAuth.Roles = new List<string>();
userAuth.Roles.Add(missingRole);
}
}
if (!permissions.IsEmpty())
{
foreach (var missingPermission in permissions.Where(x => userAuth.Permissions == null || !userAuth.Permissions.Contains(x)))
{
if (userAuth.Permissions == null)
userAuth.Permissions = new List<string>();
userAuth.Permissions.Add(missingPermission);
}
}
SaveUserAuth(userAuth, db);
}
else
{
var now = DateTime.UtcNow;
var userRoles = db.Select<UserAuthRole>(q => q.UserAuthId == userAuth.Id);
if (!roles.IsEmpty())
{
var roleSet = userRoles.Where(x => x.Role != null).Select(x => x.Role).ToHashSet();
foreach (var role in roles)
{
if (!roleSet.Contains(role))
{
db.Insert(new UserAuthRole
{
UserAuthId = userAuth.Id,
Role = role,
CreatedDate = now,
ModifiedDate = now,
});
}
}
}
if (!permissions.IsEmpty())
{
var permissionSet = userRoles.Where(x => x.Permission != null).Select(x => x.Permission).ToHashSet();
foreach (var permission in permissions)
{
if (!permissionSet.Contains(permission))
{
db.Insert(new UserAuthRole
{
UserAuthId = userAuth.Id,
Permission = permission,
CreatedDate = now,
ModifiedDate = now,
});
}
}
}
}
}
private static void AssignRolesInternal(IUserAuth userAuth, ICollection<string> roles, ICollection<string> permissions)
{
if (!roles.IsEmpty())
{
foreach (var missingRole in roles.Where(x => userAuth.Roles == null || !userAuth.Roles.Contains(x)))
{
if (userAuth.Roles == null)
userAuth.Roles = new List<string>();
userAuth.Roles.Add(missingRole);
}
}
if (!permissions.IsEmpty())
{
foreach (var missingPermission in permissions.Where(x =>
userAuth.Permissions == null || !userAuth.Permissions.Contains(x)))
{
if (userAuth.Permissions == null)
userAuth.Permissions = new List<string>();
userAuth.Permissions.Add(missingPermission);
}
}
}
public static void SaveUserAuth(IUserAuth userAuth, IDbConnection db)
{
if (userAuth == null)
throw new ArgumentNullException(nameof(userAuth));
userAuth.ModifiedDate = DateTime.UtcNow;
if (userAuth.CreatedDate == default(DateTime))
userAuth.CreatedDate = userAuth.ModifiedDate;
db.Save((UserAuthCustom)userAuth);
}
}
}
Then just call it like:
var created = UserHelper.CreateUserAuth(newUser, request.Password, Db);
UserHelper.AssignRoles(AuthRepository, created, Db, new List<string> { request.CreateUserType.ToString() });
It will only use the connection passed. Just change UserAuthCustom to whatever you user class is.

Azure Table Storage not saving all object properties

I have a problem with the Azure Table Storage. What I'm trying to achieve is saving the ChangeToken of the SharePoint list in order to use the webhooks properly.
Here is the code:
public class TablesHelper
{
private static readonly string TokenTableName = "TokenTable";
public static async Task<ListChangeToken> GetChangeTokenForListAsync(string listId)
{
var retrieveOperation = TableOperation.Retrieve<ListChangeToken>("Lists", listId, new List<string>() { "ChangeToken" });
var tableReference = await GetTableReferenceAsync(TokenTableName);
var tableResult = await tableReference.ExecuteAsync(retrieveOperation);
if(tableResult.Result != null)
{
return tableResult.Result as ListChangeToken;
}
return null;
}
public static async Task SaveChangeTokenForListAsync(ListChangeToken changeToken)
{
var insertOperation = TableOperation.Insert(changeToken);
var tableReference = await GetTableReferenceAsync(TokenTableName);
var result = await tableReference.ExecuteAsync(insertOperation);
}
private static async Task<CloudTable> GetTableReferenceAsync(string tableName)
{
var storageAccount = CloudStorageAccount.Parse(ConfigurationHelper.CloudStorage);
var tableClient = storageAccount.CreateCloudTableClient();
var reference = tableClient.GetTableReference(tableName);
await reference.CreateIfNotExistsAsync();
return reference;
}
}
The ListChangeToken class:
public class ListChangeToken : TableEntity
{
public ListChangeToken(string listId, string changeToken)
{
this.PartitionKey = "Lists";
this.RowKey = listId;
this.ChangeToken = changeToken;
}
public ListChangeToken() { }
public string ChangeToken { get; set;}
}
As per request, the function calling TablesHelper:
[FunctionName("EventHandler")]
public static async Task Run([QueueTrigger("events", Connection = "CloudStorage")]string myQueueItem, TraceWriter log)
{
var notificationGroup = Newtonsoft.Json.JsonConvert.DeserializeObject<NotificationGroup>(myQueueItem);
var contextHelper = new ContextHelper();
foreach (var notification in notificationGroup.Value)
{
UriBuilder uriBuilder = new UriBuilder();
uriBuilder.Scheme = "https";
uriBuilder.Host = ConfigurationHelper.TenantDomain;
uriBuilder.Path = notification.SiteUrl;
using (var ctx = contextHelper.GetAppOnlyContext(uriBuilder.ToString()))
{
//Read change token
var currentChangeToken = await TablesHelper.GetChangeTokenForListAsync(notification.Resource);
if(currentChangeToken == null)
{
log.Error($"No change token found for list {notification.Resource}. This is a NO GO. Please use the '/api/Setup' function.");
}
var listId = Guid.Parse(notification.Resource);
var changes = await CSOMHelper.GetListItemChangesAsync(ctx, listId, currentChangeToken.ChangeToken);
if(changes.Count > 0)
{
var lastChange = changes[changes.Count - 1];
//Save the last change token
var changeTokenValue = lastChange.ChangeToken.StringValue;
await TablesHelper.SaveChangeTokenForListAsync(new ListChangeToken(
notification.Resource,
changeTokenValue
));
await HandleChanges(ctx, changes);
}
}
}
log.Info($"C# Queue trigger function processed: {myQueueItem}");
}
The problem is that always, when using the "GetChangeTokenForListAsync" the Entity is received properly, but the .ChangeToken property is always null. It is also not visible when browsing with the Azure Storage Explorer. What am I doing wrong here?
The issue is related to the Azure Storage Emulator (V. 5.7.0.0). The same code works perfectly when working with the "live" Azure.

How to find object type from URL SharePoint online (o365)?

I found out how to determine the object type from URL for SharePoint on prem:
https://blogs.msdn.microsoft.com/sanjaynarang/2009/04/06/find-sharepoint-object-type-from-url/
But I didn't find anything for SharePoint Online (CSOM).
Is it possible for SharePoint online?
For the most scenarios such as:
folder url, e.g. https://contoso.sharepoint.com//Documents/Forms/AllItems.aspx?RootFolder=%2FDocuments%2FArchive
list item url, e.g. https://contoso.sharepoint.com/Lists/ShoppingCart/DispForm.aspx?ID=9
list/library url, e.g. https://contoso.sharepoint.com/Lists/Announcements
page url, e.g. https://contoso.sharepoint.com/Lists/Announcements/Newsletter.aspx
the following example demonstrates how to determine client object type:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Web;
using Microsoft.SharePoint.Client;
namespace O365Console
{
static class ClientObjectExtensions
{
public static ClientObject ResolveClientObjectFromUrl(string resourceUrl, ICredentials credentials)
{
ClientObject targetObject = null;
var resourceUri = new Uri(resourceUrl);
using (var rootCtx = new ClientContext(resourceUri.Scheme + Uri.SchemeDelimiter + resourceUri.Host))
{
rootCtx.Credentials = credentials;
var webUrl = Web.WebUrlFromPageUrlDirect(rootCtx, resourceUri);
using (var ctx = new ClientContext(webUrl.ToString()))
{
ctx.Credentials = credentials;
var queryBag = System.Web.HttpUtility.ParseQueryString(resourceUri.Query);
if (queryBag["Id"] != null)
{
var listUrl = string.Join(string.Empty,
resourceUri.Segments.Take(resourceUri.Segments.Length - 1));
var list = ctx.Web.GetList(listUrl);
targetObject = TryRetrieve(() => list.GetItemById(Convert.ToInt32(queryBag["Id"])));
}
else if (queryBag["RootFolder"] != null)
{
var folderUrl = HttpUtility.UrlDecode(queryBag["RootFolder"]);
targetObject = TryRetrieve(() => ctx.Web.GetFolderByServerRelativeUrl(folderUrl));
}
else if (queryBag.Count > 0)
{
throw new Exception("Unsupported query string parameter found");
}
else
{
targetObject = TryRetrieve(() => ctx.Web.GetFileByServerRelativeUrl(resourceUri.AbsolutePath));
if (targetObject == null)
{
targetObject = TryRetrieve(() => ctx.Web.GetList(resourceUri.AbsolutePath),list => list.RootFolder);
if (targetObject == null || ((List)targetObject).RootFolder.ServerRelativeUrl != resourceUri.AbsolutePath)
targetObject = TryRetrieve(() => ctx.Web.GetFolderByServerRelativeUrl(resourceUri.AbsolutePath));
}
}
}
}
return targetObject;
}
private static T TryRetrieve<T>(Func<T> loadMethod, params Expression<Func<T,object>>[] retrievals) where T : ClientObject
{
try
{
var targetObject = loadMethod();
targetObject.Context.Load(targetObject, retrievals);
targetObject.Context.ExecuteQuery();
return targetObject;
}
catch
{
}
return default(T);
}
}
}
Usage
var credentials = GetCredentials(userName, password);
var clientObj = ClientObjectExtensions.ResolveClientObjectFromUrl("https://contoso.sharepoint.com/Lists/Announcements", credentials);
Console.WriteLine(clientObj.GetType().Name);
where
static ICredentials GetCredentials(string userName,string password)
{
var securePassword = new SecureString();
foreach (var c in password)
{
securePassword.AppendChar(c);
}
return new SharePointOnlineCredentials(userName, securePassword);
}

Need help to deal with Photo for Xamarin forms

I am using xamarin forms. I want to pick photo from gallery for my iphone app and want to save it in Azure DB. Is there any solution available for xamarin forms. Or Is there any plugin available to deal with Photo, Document, or Audio. Any help is appreciated.
Using dependency service you can take or pick photos from Android / iPhone :-
Please refer to code below and try to implement the similar code:-
This is the interface in PCL:-
public interface IGalleryProvider
{
Task<List<AttachmentMediaFile>> PickPhotoAsync();
Task<List<AttachmentMediaFile>> PickAudioAsync();
Task<List<AttachmentMediaFile>> PickDocumentAsync();
Task<AttachmentMediaFile> PickProfilePhotoAsync();
Task SaveToGalleryAsync(AttachmentMediaFile file);
}
Below is the code using which you can pick or take photos from iPhone only:-
using AssetsLibrary;
using AVFoundation;
using ELCImagePicker;
using Foundation;
using MediaPlayer;
using MobileCoreServices;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UIKit;
[assembly: Xamarin.Forms.Dependency(typeof(GalleryProvider))]
namespace SanketSample.MobileApp.Sample.iOS.Common
{
public class GalleryProvider : IGalleryProvider
{
private TaskCompletionSource<List<AttachmentMediaFile>> _audioPickedTask;
public async Task<List<AttachmentMediaFile>> PickAudioAsync()
{
_audioPickedTask = new TaskCompletionSource<List<AttachmentMediaFile>>();
var picker = new MPMediaPickerController();
ShowViewController(picker);
picker.ItemsPicked += OnAudioPicked;
picker.DidCancel += OnCancel;
var media = await _audioPickedTask.Task;
return media;
}
private void OnCancel(object sender, EventArgs e)
{
var picker = sender as MPMediaPickerController;
picker.DidCancel -= OnCancel;
picker.DismissViewController(true, null);
_audioPickedTask.TrySetResult(new List<AttachmentMediaFile>());
}
private void OnAudioPicked(object sender, ItemsPickedEventArgs e)
{
var media = new List<AttachmentMediaFile>();
var picker = sender as MPMediaPickerController;
picker.ItemsPicked -= OnAudioPicked;
picker.DismissViewController(true, null);
if (e.MediaItemCollection.Items != null)
{
foreach (var item in e.MediaItemCollection.Items)
{
//var vm1 = (new ViewModelLocator()).AttachmentsVM.SelectedAttachments.Add();
if (!item.IsCloudItem)
{
try
{
//var error = new NSError();
//var asset = new AVUrlAsset(item.AssetURL);
//var exporter = new AVAssetExportSession(asset, item.Title);
//exporter.OutputFileType = "com.apple.m4a-audio";
//AVAssetExportSession session = new AVAssetExportSession(asset, "");
//var reader = new AVAssetReader(asset, out error);
//var settings = new NSDictionary();
//Func<byte[]> bytesGetter = e.MediaItemCollection
//TODO item.Title, item.Title SSSanket,
//var _asset = AVAsset.FromUrl(NSUrl.FromFilename(item.AssetURL.ToString()));
//var _exportSession = new AVAssetExportSession(_asset, AVAssetExportSession.PresetPassthrough);
//_exportSession.OutputFileType = AVFileType.Aiff;
// media.Add(new AttachmentMediaFile(item.AssetURL.AbsoluteString, AttachmentMediaFileType.Audio, null , item.Title));
}
catch (Exception ex)
{
// throw ;
}
}
}
}
_audioPickedTask.TrySetResult(media);
}
public async Task<List<AttachmentMediaFile>> PickDocumentAsync()
{
var task = new TaskCompletionSource<List<AttachmentMediaFile>>();
var allowedUTIs = new string[]
{
UTType.UTF8PlainText,
UTType.PlainText,
UTType.RTF,
UTType.Text,
UTType.PDF,
"com.microsoft.word.doc",
"com.microsoft.excel.xls"
};
var pickerMenu = new UIDocumentMenuViewController(allowedUTIs, UIDocumentPickerMode.Open);
pickerMenu.DidPickDocumentPicker += (sender, args) =>
{
args.DocumentPicker.DidPickDocument += (sndr, pArgs) =>
{
var securityEnabled = pArgs.Url.StartAccessingSecurityScopedResource();
NSError err;
var fileCoordinator = new NSFileCoordinator();
var docs = new List<AttachmentMediaFile>();
// Read bytes.
fileCoordinator.CoordinateRead(pArgs.Url, 0, out err, (NSUrl newUrl) =>
{
NSData data = NSData.FromUrl(newUrl);
docs.Add(new AttachmentMediaFile(pArgs.Url.AbsoluteString, AttachmentMediaFileType.Doc, data.ToArray(),null));
task.TrySetResult(docs);
});
};
ShowViewController(args.DocumentPicker);
};
ShowViewController(pickerMenu);
return await task.Task;
}
public async Task<List<AttachmentMediaFile>> PickPhotoAsync()
{
var media = new List<AttachmentMediaFile>();
var picker = ELCImagePickerViewController.Instance;
picker.MaximumImagesCount = 15;
ShowViewController(picker);
await picker.Completion.ContinueWith(result =>
{
picker.BeginInvokeOnMainThread(() =>
{
picker.DismissViewController(true, null);
if (!result.IsCanceled && result.Exception == null)
{
var imageEditor = new ImageEditor();
var items = result.Result as List<AssetResult>;
foreach (var item in items)
{
var bbytes= imageEditor.ResizeImage(item.Image, 1024, 1024);
media.Add(new AttachmentMediaFile(item.Path, AttachmentMediaFileType.Photo, bbytes, item.Name));
}
}
});
});
return media;
}
public async Task<AttachmentMediaFile> PickProfilePhotoAsync()
{
AttachmentMediaFile selectMediaFile = null;
var picker = ELCImagePickerViewController.Instance;
picker.MaximumImagesCount = 1;
ShowViewController(picker);
await picker.Completion.ContinueWith(result =>
{
picker.BeginInvokeOnMainThread(() =>
{
picker.DismissViewController(true, null);
if (!result.IsCanceled && result.Exception == null)
{
var imageEditor = new ImageEditor();
var items = result.Result as List<AssetResult>;
foreach (var item in items)
{
var bbytes = imageEditor.ResizeImage(item.Image, 1024, 1024);
selectMediaFile = new AttachmentMediaFile(item.Path, AttachmentMediaFileType.Photo, bbytes, item.Name);
}
}
});
});
return selectMediaFile;
}
public async Task SaveToGalleryAsync(AttachmentMediaFile file)
{
var bytes = file.GetBytes();
var originalImage = ImageEditor.ImageFromByteArray(bytes);
var library = new ALAssetsLibrary();
var orientation = (ALAssetOrientation)originalImage.Orientation;
var nsUrl = await library.WriteImageToSavedPhotosAlbumAsync(originalImage.CGImage, orientation);
}
private void ShowViewController(UIViewController controller)
{
var topController = UIApplication.SharedApplication.KeyWindow.RootViewController;
while (topController.PresentedViewController != null)
{
topController = topController.PresentedViewController;
}
topController.PresentViewController(controller, true, null);
}
}
}
Below are useful classes :-
public class AttachmentMediaFile
{
private readonly Func<byte[]> _bytesGetter;
public string LocalPath { get; private set; }
public string Name { get; private set; }
public AttachmentMediaFileType Type { get; private set; }
public AttachmentMediaFile(string localPath, AttachmentMediaFileType type, byte[] bytesGetter, string name = null)
{
LocalPath = localPath;
Type = type;
_bytesGetter = () =>
{
return bytesGetter;
};
if (string.IsNullOrEmpty(name))
{
Name = FileNameHelper.PrepareName(localPath);
}
else
{
Name = name;
}
}
public byte[] GetBytes()
{
return _bytesGetter();
}
}
public enum AttachmentMediaFileType
{
Photo = 0,
Audio = 1,
Doc = 2,
Video = 3,
}
public static class FileNameHelper
{
private const string Prefix = "IMG";
public static string PrepareName(string localPath)
{
var name = string.Empty;
if (!string.IsNullOrEmpty(localPath))
{
name = localPath.Split('/').Last();
}
return name;
}
public static string GenerateUniqueFileName(Extension extension)
{
var format = ".jpg";
var fileName = string.Concat(Prefix, '_', DateTime.UtcNow.Ticks, format);
return fileName;
}
public enum Extension
{
JPG
}
}
Now if you want to store your data to Azure Server Table so you are already using Azure mobile service client SDK similarly you need Blob nuget from Azure using which you can save your photos by making blob objects to Azure server :-
use blob helper nuget from manage nuget package install Microsoft.WindowsAzure.Storage.Auth;
Microsoft.WindowsAzure.Storage.Blob;
this and try to implement the code similarly I given bellow:-
using Acr.UserDialogs;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using SanketSample.MobileApp.sample.Business.Azure;
using SanketSample.MobileApp.sample.Business.Interfaces;
using SanketSample.MobileApp.sample.Models;
using SanketSample.MobileApp.sample.Models.AzureTables;
using SanketSample.MobileApp.sample.Models.Media;
using SanketSample.MobileApp.sample.Utils;
using Xamarin.Forms;
namespace SanketSample.MobileApp.Sanket.Common.Media
{
public class BlobHelper
{
private const string ContainerName = "attachments";
private Dictionary<string, TaskCompletionSource<bool>> _tasks;
private IHttpService _httpservice { get; set; }
#region Singleton Implementation
private static readonly Lazy<BlobHelper> lazyInstance = new Lazy<BlobHelper>(() => new BlobHelper(), true);
private BlobHelper()
{
_tasks = new Dictionary<string, TaskCompletionSource<bool>>();
}
public static BlobHelper Instance
{
get { return lazyInstance.Value; }`enter code here`
}
#endregion Singleton Implementation
public async Task UploadAttachments(IList<AttachmentFile> attachments, long associatedRecordId, string category)
{
foreach (var attachment in attachments)
{
await UploadAttachment(attachment, associatedRecordId, category);
}
}
public async Task UploadAttachment(AttachmentFile attachment, long associatedRecordId, string category)
{
try
{
CommonHelper commonHelper = new CommonHelper();
attachment.ContainerName = ContainerName;
attachment.AssociatedRecordId = associatedRecordId;
//attachment.RecordId = commonHelper.GenerateRecordId();
if (attachment.FileExtension == null)
{
attachment.FileExtension = ConvertType(attachment.MediaFile);
}
attachment.Category = category;
var taskCompletionSource = new TaskCompletionSource<bool>();
if (!_tasks.ContainsKey(attachment.Name))
{ _tasks.Add(attachment.Name, taskCompletionSource); }
else
{
_tasks[attachment.Name] = taskCompletionSource;
}
// _tasks.Add(attachment.Name, taskCompletionSource);
var attachmentsTableOnline = AzureServiceProvider.Instance.GetRemoteTable<AttachmentFile>();
if (CheckInternetConnection.IsConnected())
{
await attachmentsTableOnline.InsertAsync(attachment);
}
var attachmentsTableOffline = AzureServiceProvider.Instance.GetLocalTable<AttachmentFile>();
await attachmentsTableOffline.InsertAsync(attachment);
if (!string.IsNullOrEmpty(attachment.SasQueryString))
{
var credentials = new StorageCredentials(attachment.SasQueryString);
var imageUri = new Uri(attachment.Uri);
var container = new CloudBlobContainer(new Uri(string.Format("https://{0}/{1}",
imageUri.Host, attachment.ContainerName)), credentials);
var blobFromSASCredential = container.GetBlockBlobReference(attachment.Name);
try
{
var bytes = attachment.MediaFile.GetBytes();
await blobFromSASCredential.UploadFromByteArrayAsync(bytes, 0, bytes.Length);
if (CheckInternetConnection.IsConnected())
{
await attachmentsTableOnline.UpdateAsync(attachment);
}
await attachmentsTableOffline.UpdateAsync(attachment);
taskCompletionSource.TrySetResult(true);
}
catch (Microsoft.WindowsAzure.Storage.StorageException ex)
{
// Throws from UploadFromByteArrayAsync, but image uploaded.
System.Diagnostics.Debug.WriteLine($"BlobHelper: {ex}");
taskCompletionSource.TrySetResult(true);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"BlobHelper: {ex}");
taskCompletionSource.TrySetResult(false);
}
}
}
catch (Exception ca)
{
//throw ca;
}
}
/// <summary>
/// Downloads Blob Data boject and returns the Byts[] data
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public async Task<byte[]> DownloadAttachment(AttachmentFile file)
{
byte[] bytes = null;
var fileContainer = file.Uri.Replace(file.Name, string.Empty);
var container = new CloudBlobContainer(new Uri(fileContainer));
var blob = container.GetBlockBlobReference(file.Name);
using (var stream = new MemoryStream())
{
var isExist = await blob.ExistsAsync();
if (isExist)
{
await blob.DownloadToStreamAsync(stream);
bytes = stream.ToArray();
}
}
return bytes;
}
/// <summary>
/// Updates the Attachments Byts in the Azure Local Tables.
/// </summary>
/// <param name="AttachmentFileRecordId">Attachments Byte[] Data.</param>
/// <returns></returns>
public async Task<byte[]> DownloadAttachmentFileDetails(long? AttachmentFileRecordId, IHttpService service)
{
_httpservice = service;
try
{
ResponseWrapper<AttachmentFileDetail> result = new ResponseWrapper<AttachmentFileDetail>();
if (AttachmentFileRecordId != null)
{
var request = Constants.API_BASE_URL + string.Format(Constants.API_ATTACHMENTS_PARAMETERS, AttachmentFileRecordId);
var response = await _httpservice.SendRequestAsync(HttpMethod.Get, request);
result.Status = response.Status;
if (response.IsSuccess)
{
result.Result = JsonConvert.DeserializeObject<AttachmentFileDetail>(response.Result);
if (result.Result == null)
{
result.Status = System.Net.HttpStatusCode.InternalServerError;
}
else
{
var output = result.Result;
var data = new List<AttachmentFileDetail>() { output };
await AzureServiceProvider.Instance.DatabaseService.InsertDataToLocalDB<AttachmentFileDetail>(data);
return result.Result.FileByteArray;
}
}
}
}
catch (Exception ex)
{
////throw ex;
}
finally
{
}
return null;
}
private string ConvertType(AttachmentMediaFile file)
{
switch (file.Type)
{
case AttachmentMediaFileType.Doc:
return "doc";
case AttachmentMediaFileType.Audio:
return "mp3";
}
return "jpeg";
}
}
}
media plugin on github
works pretty well for me.

Resources