How to stream Video that are stored in Azure storage Blob using Xamarin - azure

I have uploaded video files to Azur Blob(Containers),I want to access them in the mobile app via Streaming.I have files with extention .mp4 . I have done code to download from blob and Store in local drive then play using default player , But I want to give user a option to stream instead of download.
I have used this method
var credentials = new StorageCredentials("myaccountname", "mysecretkey");
var account = new CloudStorageAccount(credentials, true);
var container = account.CreateCloudBlobClient().GetContainerReference("yourcontainername");
var blob = container.GetBlockBlobReference("yourmp4filename");
var sas = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()
{
Permissions = SharedAccessBlobPermissions.Read,
SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),//Set this date/time according to your requirements
});
var urlToBePlayed = string.Format("{0}{1}", blob.Uri, sas);//This is the URI which should be embedded in your video player.
Problem:-
If I browse the Url(Blob Url) , It downloads the file instead of directly playing it .But in App , Nothing appears. Blank screen.
I am using
<WebView Source="{Binding VideoUrl}" HeightRequest="200" WidthRequest="200"/>
In Vm:
VideoUrl=url;

First Change content Type : like #Zhaoxing Lu - Microsoft said
public async Task ChangeContentTypeAsync()
{
try
{
UserDialogs.Instance.ShowLoading();
BlobContinuationToken blobContinuationToken = null;
var storageAccount = CloudStorageAccount.Parse(storageConnectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("videos");
var results = await container.ListBlobsSegmentedAsync(null, blobContinuationToken);
blobContinuationToken = results.ContinuationToken;
BlobResultSegment blobs = await blobClient
.GetContainerReference("videos")
.ListBlobsSegmentedAsync(blobContinuationToken)
;
foreach (CloudBlockBlob blob in blobs.Results)
{
if (Path.GetExtension(blob.Uri.AbsoluteUri) == ".mp4")
{
blob.Properties.ContentType = "video/mp4";
}
//// repeat and resume
await blob.SetPropertiesAsync();
}
UserDialogs.Instance.HideLoading();
}
catch (Exception ex)
{
var m = ex.Message;
}
}
Then Use this method :
private async Task StreamVideo(string filename)
{
try
{
UserDialogs.Instance.ShowLoading();
await azureBlob.ChangeContentTypeAsync();
var secretkey = "xxxx";
var credentials = new StorageCredentials("chatstorageblob", secretkey);
var account = new CloudStorageAccount(credentials, true);
var container = account.CreateCloudBlobClient().GetContainerReference("videos");
var blob = container.GetBlockBlobReference(filename);
var sas = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()
{
Permissions = SharedAccessBlobPermissions.Read,
SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),//Set this date/time according to your requirements
});
var urlToBePlayed = string.Format("{0}{1}", blob.Uri, sas);//This is the URI which should be embedded in your video player.
await Navigation.PushAsync(new VideoPlayerPage(urlToBePlayed));
UserDialogs.Instance.HideLoading();
}
catch (Exception ex)
{
var m = ex.Message;
}
}

Related

Migrate from Azure to SharePoint using Migration API - "Must specify IV in Manifest or blob metadata'"

I am trying to use the Migration API with C# to migrate from the default free Azure Cloud (given to all sharepoint accounts) to SharePoint.
I can successfully generate a cloud container, and upload files, but the migration fails. Here is my code:
private void MigrateToAzureThenSharePoint()
{
//Provision The default Azure Containers and Migation Queue
var migrationContainers = destinationCtx.Site.ProvisionMigrationContainers();
var migrationQueue = destinationCtx.Site.ProvisionMigrationQueue();
destinationCtx.ExecuteQuery();
var containerInfoList = migrationContainers.Value;
var dataContainerUri = containerInfoList.DataContainerUri;
var metadataContainerUri = containerInfoList.MetadataContainerUri;
var queueInfo = migrationQueue.Value;
//Instansiate Cloud Queue
var cloudQueue = new CloudQueue(new Uri(queueInfo.JobQueueUri));
//Instansiate CloudBlobContainer
CloudBlobContainer dataContainer = new CloudBlobContainer(new Uri(dataContainerUri));
CloudBlobContainer manifestContainer = new CloudBlobContainer(new Uri(metadataContainerUri));
//Test files to upload
var testfiles = new[]
{
new SourceFile
{
Filename = "test.txt",
LastModified = DateTime.Now,
Contents = Encoding.UTF8.GetBytes("Hi, this is a test text-file"),
Title = "Title of file 1"
},
new SourceFile
{
Filename = "test2.txt",
LastModified = DateTime.Now.AddDays(-1),
Contents = Encoding.UTF8.GetBytes("Tesfile2"),
Title = "Second title"
}
};
//Upload Test Files to Cloud
foreach (var testfile in testfiles)
{
var blobReference = dataContainer.GetBlockBlobReference(testfile.Filename);
blobReference.UploadFromByteArray(testfile.Contents, 0, testfile.Contents.Length, null);
blobReference.CreateSnapshot();
}
var manifestPackage = new ManifestPackage(destOrganisationList.Id,
destinationCtx.Web.Id,
destinationCtx.Web.Title,
destOrganisationList.Title,
#"\",
destOrganisationList.RootFolder.UniqueId,
destOrganisationList.RootFolder.ParentFolder.UniqueId
);
//Create XML Files for Azure Migration (An easier way here would be great? I manualy write them)
var filesInManifestPackage = manifestPackage.GetManifestPackageFiles(testfiles);
//Uploaded manually created XML files
foreach (var migrationPackageFile in filesInManifestPackage)
{
var blobReference = manifestContainer.GetBlockBlobReference(migrationPackageFile.Filename);
blobReference.UploadFromByteArray(migrationPackageFile.Contents, 0, migrationPackageFile.Contents.Length, null);
blobReference.CreateSnapshot();
}
//Start Migration
var result = destinationCtx.Site.CreateMigrationJobEncrypted(destinationCtx.Web.Id,
containerInfoList.DataContainerUri,
containerInfoList.MetadataContainerUri,
queueInfo.JobQueueUri,
new EncryptionOption()
{
AES256CBCKey = containerInfoList.EncryptionKey
});
destinationCtx.ExecuteQuery();
//Check for messags
while (true) {
var msg = cloudQueue.GetMessage();
if (msg == null)
{
Task.Delay(TimeSpan.FromSeconds(1));
continue;
}
var message = JsonConvert.DeserializeObject<EncryptedMessage>(msg.AsString);
cloudQueue.DeleteMessage(msg);
//Decode encrypted message
var decoded = JsonConvert.DeserializeObject<UpdateMessage>(Decrypt(message.Content, containerInfoList.EncryptionKey, message.IV));
//Error
}
}
There are a few errors, but they all are similar to this:
"Unable to download SystemData.xml with exception 'Must specify IV in
Manifest or blob metadata'"
I believe I may have to encrypt the files when I upload from this method:
blobReference.UploadFromByteArray
There is an option:
var blobRequestOptions = new BlobRequestOptions();
blobRequestOptions.EncryptionPolicy =
But I don't know how to instantiate the encryption policy (and I may be going down a rabbit hole)
Any help would be greatly appreciated, as documentation online is shocking minimal.

Download Zip file from azure Storage c#

I am looping through the file names from my database and the same file i have in azure storage. I am zipping those n number of files and download from azure storage. I saves the zipped file to my local storage. When i extract and want to see a file, it say damaged/corrupt.
public ActionResult Download(string productid, string YearActiveid)
{
HomePageModel homepagemodel = new HomePageModel();
homepagemodel.ProdHeaderDetail = GetProductHeaderDetail(productid, YearActiveid);
homepagemodel.PriorYearsActive = GetPriorYearActive(productid, YearActiveid);
CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=<name>;AccountKey=<key>;EndpointSuffix=core.windows.net");
CloudBlobClient cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference("product");
var blobFileNames = new string[] { "file1.png", "file2.png", "file3.png", "file4.png" };
var outputMemStream = new MemoryStream();
var zipOutputStream = new ZipOutputStream(outputMemStream);
foreach (var ProdHeaderDetail in homepagemodel.ProdHeaderDetail)
{
zipOutputStream.SetLevel(5);
var blob = cloudBlobContainer.GetBlockBlobReference(ProdHeaderDetail.FileName);
var entry = new ZipEntry(ProdHeaderDetail.FileName);
zipOutputStream.PutNextEntry(entry);
blob.DownloadToStreamAsync(zipOutputStream);
}
zipOutputStream.Finish();
//zipOutputStream.Close();
//zipOutputStream.CloseEntry();
zipOutputStream.IsStreamOwner = false;
outputMemStream.Position = 0;
return File(outputMemStream, "application/zip", "filename.zip");
}
I resolved the issue by adding async and wait
public async Task Download(string productid, string YearActiveid)
await blob.DownloadToStreamAsync(zipOutputStream);

Email Attachments missing on Azure, works locally

I have implemented something similar to the following question and I can get it working locally on my server, but when I deploy to Azure it doesn't work. I don't get any errors: just an email without the attachment.
Sending attachments using Azure blobs
Are there restrictions on what types of files can be sent with SendGrid (file is only 56k)?
Does the Azure App service have to be at a particular level or can it be done on Basic?
The blob URL definitley exists and I am setting the stream to zero as suggested in that previous question.
MailMessage mm = new MailMessage("receiver address", "someone");
mm.From = new MailAddress("myAddress", "My Name");
mm.Subject = content.Subject;
mm.Body = content.Body;
mm.IsBodyHtml = true;
mm.BodyEncoding = UTF8Encoding.UTF8;
mm.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;
var attachmentAsStream = _storageAccessService.GetAssetAsStreamForEmail("myContainer", "fileThatExists.pdf");
var attachment = new Attachment(attachmentAsStream, "File.pdf", MediaTypeNames.Application.Pdf);
mm.Attachments.Add(attachment);
public MemoryStream GetAssetAsStreamForEmail(string containerName, string fileName)
{
// Create the blob client.
CloudBlobClient blobClient = StorageAccountReference().CreateCloudBlobClient();
// Retrieve reference to a previously created container.
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
var memoryStream = new MemoryStream();
try
{
using (var stream = new MemoryStream())
{
blob.DownloadToStream(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
}
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Failed to download Email Atatchment: "+ ex.Message));
}
memoryStream.Position = 0;
return memoryStream;
}
using (SmtpClient client = new SmtpClient())
{
client.Port = 587;
client.Host = "smtp.sendgrid.net";
client.EnableSsl = true;
client.Timeout = 10000;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential("hidden", "hidden");
await client.SendMailAsync(message);
}
Update
Update after Yasir's suggestion below. Downloading the blob from Azure as a Stream only seems to work locally. But if I change to download as a ByteArray then it works everywhere, nonetheless...
public MemoryStream GetAssetAsStreamForEmail(string containerName, string fileName)
{
// Create the blob client.
CloudBlobClient blobClient = StorageAccountReference().CreateCloudBlobClient();
// Retrieve reference to a previously created container.
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
try
{
blob.FetchAttributes();
var fileStream = new byte[blob.Properties.Length];
for (int i = 0; i < blob.Properties.Length; i++)
{
fileStream[i] = 0x20;
}
blob.DownloadToByteArray(fileStream, 0) ;
MemoryStream bufferStream = new MemoryStream(fileStream);
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Failed to download Email Atatchment: " + ex.Message));
}
return null;
}
The following code works for me for sending emails using Sendgrid from Azure functions. I attach a CSV file from Blob Storage. It should work for you as well. All you will have to do is ensure that you read the pdf file as a byte[].
public interface IEmailAttachment
{
string Name { get; }
byte[] FileData { get; }
}
public static void Send(MailMessage mailMessage, IEnumerable<IEmailAttachment> attachments)
{
try
{
// Get the configuration data
string server = ConfigReader.EmailServer;
int port = ConfigReader.EmailPort;
string username = ConfigReader.SendGridUserName;
string password = ConfigReader.SendGridPassword;
smtpClient.EnableSsl = false;
smtpClient.Credentials = new NetworkCredential(username, password);
// Create the SMTP Client
SmtpClient smtpClient = new SmtpClient(server, port);
// Prepare the MailMessage
mailMessage.From = new MailAddress(ConfigReader.FromEmail);
var toEmails = ConfigReader.ToEmail.Split(',');
foreach (var toEmail in toEmails)
{
mailMessage.To.Add(toEmail);
}
var ccEmails = ConfigReader.EmailCc.Split(',');
foreach (var ccEmail in ccEmails)
{
mailMessage.CC.Add(ccEmail);
}
// Add attachments
List<MemoryStream> files = new List<MemoryStream>();
if (attachments != null)
{
foreach (IEmailAttachment file in attachments)
{
MemoryStream bufferStream = new MemoryStream(file.FileData);
files.Add(bufferStream);
Attachment attachment = new Attachment(bufferStream, file.Name);
mailMessage.Attachments.Add(attachment);
}
}
mailMessage.IsBodyHtml = true;
// Send the email
smtpClient.Send(mailMessage);
foreach (MemoryStream stream in files)
{
stream.Dispose();
}
}
catch (Exception)
{
throw;
}
}

How to download image to Xamarin.forms listview

I have a image url in list view and after click to listview image should download on same row.
How can I achieve on Xamarin.forms?
I got image from Azure blob
I'd recommend checking this blog post using Xamarin Forms Listview with SQL and Blob storage. They basically used SQL to store the images from Blob storage using the bellow code:
public class AzureMediaStorageService : IMediaStorageService
{
private CloudBlobContainer MediaContainer { get; set; }
public void InitializeStorage(string settings = null)
{
if (MediaContainer != null)
return;
if (string.IsNullOrEmpty(settings))
throw new ArgumentNullException(nameof(settings), "Azure Storage Media Service needs a connection string name as \"settings\" parameter");
var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings[settings].ConnectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
MediaContainer = blobClient.GetContainerReference("medias");
MediaContainer.CreateIfNotExists();
MediaContainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
}
public async Task<Uri> UploadBytesAsync(byte[] bytes, string uniqueId, MediaType mediaType)
{
var blockBlob = MediaContainer.GetBlockBlobReference(uniqueId);
blockBlob.Properties.ContentType = mediaType == MediaType.Picture ? "image/jpeg" : "video/mp4";
await blockBlob.UploadFromByteArrayAsync(bytes, 0, bytes.Length);
return blockBlob.Uri;
}
public async Task<byte[]> DownloadBytesAsync(Uri uri)
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseContentRead);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
}
public string GetSecureClientUploadUrl(string uniqueId, DateTimeOffset expirationDateTime)
{
if (expirationDateTime <= DateTimeOffset.Now)
throw new ArgumentOutOfRangeException(nameof(expirationDateTime), expirationDateTime, null);
var blob = MediaContainer.GetBlockBlobReference(uniqueId);
var sasPolicy = new SharedAccessBlobPolicy
{
SharedAccessExpiryTime = expirationDateTime,
Permissions = SharedAccessBlobPermissions.Create
};
return MediaContainer.Uri + blob.GetSharedAccessSignature(sasPolicy);
}
}
The blog post can be found here: https://forums.xamarin.com/discussion/98784/how-to-load-images-in-xamarin-forms-listview-with-asp-net-web-api-from-sql-azure-database

Azure File Share REST API for Xamarin

In Xamarin client app, I want to access Azure Files using SAS token with Portable Class Library. It seems I can not do it using latest WindowAzure.Storage nuget package as it may only works with Blob, Table,... and it requires lots of dependencies.
Is there anyway to accomplish this?
I ended up with using Azure File Storage REST API.
Basically we request SAS token generated from Azure Share first then using that SAS token in URL to send http request to Azure Files Storage:
https://[yourshare].file.core.windows.net/[yourdirectory]/[yourfile]?[your_sas_token]
I have created a class to help client doing some basic operations as below (it is portable class so can use anywhere in client side):
public class AzureFileREST
{
private AzureSASToken _azureShareToken;
public AzureFileREST(AzureSASToken azureShareToken)
{
_azureShareToken = azureShareToken;
}
public async Task CreateIfNotExist(string directoryName)
{
var existed = await CheckDirectoryExists(directoryName);
if (!existed)
{
await CreateDirectory(directoryName);
}
}
public async Task<bool> CheckDirectoryExists(string directoryName)
{
using (var client = new HttpClient())
{
//Get directory (https://msdn.microsoft.com/en-us/library/azure/dn194272.aspx)
var azureCreateDirUrl = _azureShareToken.Url + directoryName + _azureShareToken.SASToken + "&restype=directory";
var response = await client.GetAsync(azureCreateDirUrl).ConfigureAwait(false);
return (response.StatusCode != System.Net.HttpStatusCode.NotFound);
}
}
public async Task CreateDirectory(string directoryName)
{
using (var client = new HttpClient())
{
//Create directory (https://msdn.microsoft.com/en-us/library/azure/dn166993.aspx)
var azureCreateDirUrl = _azureShareToken.Url + directoryName + _azureShareToken.SASToken + "&restype=directory";
var response = await client.PutAsync(azureCreateDirUrl, null).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
}
public async Task UploadFile(string fileName, byte[] fileBytes)
{
using (var client = new HttpClient())
{
//Create empty file first (https://msdn.microsoft.com/en-us/library/azure/dn194271.aspx)
var azureCreateFileUrl = _azureShareToken.Url + fileName + _azureShareToken.SASToken;
client.DefaultRequestHeaders.Add("x-ms-type", "file");
client.DefaultRequestHeaders.Add("x-ms-content-length", fileBytes.Length.ToString());
var response = await client.PutAsync(azureCreateFileUrl, null).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
//Then upload file (https://msdn.microsoft.com/en-us/library/azure/dn194276.aspx)
var azureUploadFileUrl = azureCreateFileUrl + "&comp=range";
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("x-ms-write", "update");
client.DefaultRequestHeaders.Add("x-ms-range", String.Format("bytes=0-{0}", (fileBytes.Length - 1).ToString()));
var byteArrayContent = new ByteArrayContent(fileBytes);
response = await client.PutAsync(azureUploadFileUrl, byteArrayContent).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
}
}
In server side, use the following function to generate SAS token from Share:
public AzureSASToken GetSASFromShare(string shareName)
{
var share = _fileclient.GetShareReference(shareName);
share.CreateIfNotExists();
string policyName = "UPARSharePolicy";
// Create a new shared access policy and define its constraints.
var sharedPolicy = new SharedAccessFilePolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.AddDays(15),
Permissions = SharedAccessFilePermissions.Read | SharedAccessFilePermissions.Write
};
// Get existing permissions for the share.
var permissions = share.GetPermissions();
// Add the shared access policy to the share's policies.
// Note that each policy must have a unique name.
// Maximum 5 policies for each share!
if (!permissions.SharedAccessPolicies.Keys.Contains(policyName))
{
if (permissions.SharedAccessPolicies.Count > 4)
{
var lastAddedPolicyName = permissions.SharedAccessPolicies.Keys.Last();
permissions.SharedAccessPolicies.Remove(lastAddedPolicyName);
}
permissions.SharedAccessPolicies.Add(policyName, sharedPolicy);
share.SetPermissions(permissions);
}
var sasToken = share.GetSharedAccessSignature(sharedPolicy);
//fileSasUri = new Uri(share.StorageUri.PrimaryUri.ToString() + sasToken);
return new AzureSASToken ()
{
Name = shareName,
Url = share.StorageUri.PrimaryUri.ToString() + "/",
SASToken = sasToken
};
}
Finally using class like this:
var azureFileRest = new AzureFileREST(sasToken);
await azureFileRest.CreateIfNotExist(directoryName);
await azureFileRest.UploadFile(directoryName + "/" + fileName, bytes);

Resources