UWP - How to Save a Contact Thumbnail to Local Storage? - c#

i want some help to get the thumbnail image of a Contact and save it to Local Storage, i successfully got the contact Thumbnail but i can't get the actual image from the stream, this is my Code:
var contactStore = await ContactManager.RequestStoreAsync();
var contacts = await contactStore.FindContactsAsync();
var myContact = contacts[0]; //I am sure that this Contact has a Thumbnail
var stream = await myContact.Thumbnail.OpenReadAsync();
byte[] buffer = new byte[stream.Size];
var readBuffer = await stream.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None);
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("image.jpg", CreationCollisionOption.ReplaceExisting);
var fileStream = await file.OpenStreamForWriteAsync();
await fileStream.WriteAsync(readBuffer.ToArray(), 0, (int)readBuffer.Length);
this code creates an empty image in the local storage, any help ?
thanks for your time

The problem here is that you missed Stream.Flush method to flush the buffer to the underlying stream. You can add fileStream.Flush(); after fileStream.WriteAsync method to fix your issue.
Besides this, we also need to call Stream.Dispose Method to releases the resources used by the Stream when we finish using it. And this method disposes the stream, by writing any changes to the backing store and closing the stream to release resources. So we can just use fileStream.Dispose() after fileStream.WriteAsync method.
A recommend way to call the Dispose method is using the C# using statement like following:
var contactStore = await ContactManager.RequestStoreAsync();
var contacts = await contactStore.FindContactsAsync();
var myContact = contacts[0]; //I am sure that this Contact has a Thumbnail
using (var stream = await myContact.Thumbnail.OpenReadAsync())
{
byte[] buffer = new byte[stream.Size];
var readBuffer = await stream.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None);
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("image.jpg", CreationCollisionOption.ReplaceExisting);
using (var fileStream = await file.OpenStreamForWriteAsync())
{
await fileStream.WriteAsync(readBuffer.ToArray(), 0, (int)readBuffer.Length);
}
}

I believe you may have to call stream.Dispose() after you read from it or else initialize the stream with a using directive: using (var outputStream = stream.GetOutputStreamAt(0))
The following link might be useful:
https://msdn.microsoft.com/en-us/windows/uwp/files/quickstart-reading-and-writing-files?f=255&MSPPError=-2147217396

Related

Can save stream as local file, but when returning it as HttpResponseMessage - always empty file

I want to write export/download functionality for files from external API.
I've created separate Action for it. Using external API I can get stream for that file.
When I am saving that stream to local file, everything is fine, file isn't empty.
var exportedFile = await this.GetExportedFile(client, this.ReportId, this.WorkspaceId, export);
// Now you have the exported file stream ready to be used according to your specific needs
// For example, saving the file can be done as follows:
string pathOnDisk = #"D:\Temp\" + export.ReportName + exportedFile.FileSuffix;
using (var fileStream = File.Create(pathOnDisk))
{
await exportedFile.FileStream.CopyToAsync(fileStream);
}
But when I return exportedFile object that contains in it stream and do next:
var result = await this._service.ExportReport(reportName, format, CancellationToken.None);
var fileResult = new HttpResponseMessage(HttpStatusCode.OK);
using (var ms = new MemoryStream())
{
await result.FileStream.CopyToAsync(ms);
ms.Position = 0;
fileResult.Content = new ByteArrayContent(ms.GetBuffer());
}
fileResult.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = $"{reportName}{result.FileSuffix}"
};
fileResult.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return fileResult;
Exported file is always empty.
Is it problem with stream or with code that try to return that stream as file?
Tried as #Nobody suggest to use ToArray
fileResult.Content = new ByteArrayContent(ms.ToArray());
the same result.
Also tried to use StreamContent
fileResult.Content = new StreamContent(result.FileStream);
still empty file.
But when I'm using StreamContent and MemmoryStream
using (var ms = new MemoryStream())
{
await result.FileStream.CopyToAsync(ms);
ms.Position = 0;
fileResult.Content = new StreamContent(ms);
}
in result I got
{
"error": "no response from server"
}
Note: from 3rd party API I get stream that is readonly.
you used GetBuffer() to retrieve the data of the memory stream.
The function you should use is ToArray()
Please read the Remarks of the documentation of these functions.
https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream.getbuffer?view=net-6.0
using (var ms = new MemoryStream())
{
ms.Position = 0;
await result.FileStream.CopyToAsync(ms);
fileResult.Content = new ByteArrayContent(ms.ToArray()); //ToArray() and not GetBuffer()
}
Your "mistake" although it's an obvious one is that you return a status message, but not the actual file itself (which is in it's own also a 200).
You return this:
var fileResult = new HttpResponseMessage(HttpStatusCode.OK);
So you're not sending a file, but a response message. What I'm missing in your code samples is the procedure call itself, but since you use a HttpResonseMessage I will assume it's rather like a normal Controller action. If that is the case you could respond in a different manner:
return new FileContentResult(byteArray, mimeType){ FileDownloadName = filename };
where byteArray is ofcourse just a byte[], the mimetype could be application/octet-stream (but I suggest you'd actually find the correct mimetype for the browser to act accordingly) and the filename is the filename you want the file to be named.
So, if you were to stitch above and my comment together you'd get this:
var exportedFile = await this.GetExportedFile(client, this.ReportId, this.WorkspaceId, export);
// Now you have the exported file stream ready to be used according to your specific needs
// For example, saving the file can be done as follows:
string pathOnDisk = #"D:\Temp\" + export.ReportName + exportedFile.FileSuffix;
using (var fileStream = File.Create(pathOnDisk))
{
await exportedFile.FileStream.CopyToAsync(fileStream);
}
return new FileContentResult(System.IO.File.ReadAllBytes(pathOnDisk), "application/octet-stream") { FileDownloadName = export.ReportName + exportedFile.FileSuffix };
I suggest to try it, since you still report a 200 (and not a fileresult)

how to get Bytes of a file for sending in UWP?

I'm a newbie in UWP and i want to open a file of any type and transmit the bytes of it to the reciever. forexample for a jpg file i wrote this code:
// Create FileOpenPicker instance
FileOpenPicker fileOpenPicker = new FileOpenPicker();
// Set SuggestedStartLocation
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
// Set ViewMode
fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;
fileOpenPicker.FileTypeFilter.Clear();
fileOpenPicker.FileTypeFilter.Add(".jpg");
// Open FileOpenPicker
StorageFile file = await fileOpenPicker.PickSingleFileAsync();
byte[] bytesRead = File.ReadAllBytes(file.Path);
string Paths =
#"C:\\Users\zahraesm\Pictures\sample_reconstructed.jpg";
File.WriteAllBytes(Paths, bytesRead);
the two last lines are for writing the bytes into a file supposing in the receiver. However i keep getting the following exception:
System.InvalidOperationException: 'Synchronous operations should not be performed on the UI thread. Consider wrapping this method in Task.Run.'
Try this Code.
try {
FileOpenPicker openPicker = new FileOpenPicker {
ViewMode = PickerViewMode.Thumbnail,
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
FileTypeFilter = { ".jpg", ".jpeg", ".png" }
};
StorageFile file = await openPicker.PickSingleFileAsync();
if (file != null) {
using (IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.Read)) {
var reader = new Windows.Storage.Streams.DataReader(fileStream.GetInputStreamAt(0));
var LoadReader = await reader.LoadAsync((uint)fileStream.Size);
byte[] pixels = new byte[fileStream.Size];
reader.ReadBytes(pixels);
}
}
} catch (Exception ex) {
}
consider wrapping last operation in Task.Run()
await Task.Run(()=>{
byte[] bytesRead = File.ReadAllBytes(file.Path);
string Paths =
#"C:\\Users\zahraesm\Pictures\sample_reconstructed.jpg";
File.WriteAllBytes(Paths, bytesRead);
});
You should directly read the bytes from the StorageFile returned from your FilePicker, lest you end up with File permission errors in the future.
StorageFile file = await fileOpenPicker.PickSingleFileAsync();
var buffer = await FileIO.ReadBufferAsync(file);
byte[] bytes = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(buffer);
You should also use await FileIO.WriteBytesAsync(targetFile, myBytes) to write.
Unless you have broadFileSystemAccess in your package Manifest, you should generally avoid using the System.IO API unless you know your application explicitly has permission to access files in that area (i.e., your application's local storage), and instead use Windows.Storage API's
Check MSDN for File Access Permissions for UWP apps for more information on file permissions.
And if you do use System.IO, always perform the work on the background thread via await Task.Run(() => { ... }

Set stream for MediaElement from Http request

I have a code for recording some music/video file from online stream (video stream or radio).
How can I modify it for setting the SetSource property to my MediaElement (from IBuffer or Bytes[],... for example)?
(I cannot use the variant like this:
mediaElement1.Source = new Uri(urlLinkToOnlineStream);
mediaElement1.Play();
)
I need to set SetSource directly from already opened stream (I need write to file and record the same bytes[] from stream in the same moment or with a small pause).
HttpClientHandler aHandler = new HttpClientHandler();
aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
HttpClient aClient = new HttpClient(aHandler);
aClient.DefaultRequestHeaders.ExpectContinue = false;
HttpResponseMessage response = await aClient.GetAsync(urlLinkToOnlineStream, HttpCompletionOption.ResponseHeadersRead);
var destinationFile = await KnownFolders.MusicLibrary.CreateFileAsync(#"recorded.mp3", CreationCollisionOption.ReplaceExisting);
var fileStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite);
Stream stream = await response.Content.ReadAsStreamAsync();
IInputStream inputStream = stream.AsInputStream();
ulong totalBytesRead = 0;
while (true)
{
// Read from the web.
IBuffer buffer = new Windows.Storage.Streams.Buffer(1024);
buffer = await inputStream.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.None);
if (buffer.Length == 0)
{
break;
}
totalBytesRead += buffer.Length;
await fileStream.WriteAsync(buffer);
}
inputStream.Dispose();
fs.Dispose();
Are you developing the Universal Windows Platform App or Windows Phone 8.1 App? The UWP supports the media stream protocol like HTTP Live Stream. You can assign the URL to the media element directly.
UWP:
media.Source = new Uri("http://amssamples.streaming.mediaservices.windows.net/49b57c87-f5f3-48b3-ba22-c55cfdffa9cb/Sintel.ism/manifest(format=m3u8-aapl)");
For Windows Phone 8.1, you can use the open source stream media library.
For example: Windows Phone Streaming Media
I know this is old, however I have a working example for a single audio file that can be adapted for a stream.
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
HttpClient hc = new HttpClient();
HttpResponseMessage msg = await hc.GetAsync(urlLinkToOnlineStream);
await RandomAccessStream.CopyAsync(await msg.Content.ReadAsInputStreamAsync(), stream);
stream.Seek(0);
myMediaElement.SetSource(stream, msg.Content.Headers.ContentType.ToString());
You must remember to call Dispose() on your InMemoryRandomAccessStream and HttpClient when you are sure you have finished with them. Otherwise you will most likely end up with a memory leak.
I'm not entirely sure how you intend to do the buffering, but you if you call Dispose() on the stream (in my example) playback will fail. It will also start playing the new chunk of data if you call SetSource before current chunk has finished.

How do I get the name of a file generated with a stream in C#?

I'm building a Windows 8 metro app with XAML/C#. I'm saving an .xml file my data structure with a stream, like this:
XmlSerializer serializer = new XmlSerializer(typeof(MyObjectType));
using (var stream = await App.LocalStorage.OpenStreamForWriteAsync(MyObject.Title + ".xml", Windows.Storage.CreationCollisionOption.GenerateUniqueName))
serializer.Serialize(stream, MyObject);
Where:
App.LocalStorage
Is obviously a StorageFolder objecty set to
Windows.Storage.ApplicationData.Current.LocalFolder
The GenerateUniqueName option is set in order to avoid collisions, because my objects can have the same title. Now, I need to get the file name my stream generated, how can I get it?
Thank you
Try creating the file first.
var sourceFileName = MyObject.Title + ".xml";
StorageFile storageFile = await App.LocalStorage.CreateFileAsync(sourceFileName, Windows.Storage.CreationCollisionOption.GenerateUniqueName);
using (var stream = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
{
serializer.Serialize(stream, MyObject);
}
The OpenStreamForWriteAsync method does not seem to give you any easy way to access this information. You could switch to accessing it another way:
StorageFile file = await App.LocalStorage.CreateFileAsync(...);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
// do stuff, file name is at file.Name

Getting a Stream from a resource file / content

Is this the correct/only way of getting a Stream from a resource file?
Uri uri = new Uri(fullPath);
StorageFile storageFile =
await Windows.Storage.StorageFile.
GetFileFromApplicationUriAsync(uri);
IRandomAccessStreamWithContentType randomAccessStream =
await storageFile.OpenReadAsync();
IInputStream resourceStream = (IInputStream)
randomAccessStream.GetInputStreamAt(0);
All my other sources (http and local storage) return a Stream object, and it is painful to have to if-else code that uses one or the other.
I've also tried to just create a MemoryStream out of it, but I can't even find a way to get the bytes out... Please help.
uint size = (uint)randomAccessStream.Size;
IBuffer buffer = new Windows.Storage.Streams.Buffer(size);
await randomAccessStream.ReadAsync(buffer, size,
InputStreamOptions.None);
Stream stream = new MemoryStream(buffer); // error takes byte[] not IBuffer
IInputStream.ReadAsync() when reading from resource:
http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.streams.iinputstream.readasync.aspx
while Stream.Read() and Stream.ReadAsync() look like this:
http://msdn.microsoft.com/en-us/library/system.io.stream.read.aspx
and
http://msdn.microsoft.com/en-us/library/hh137813.aspx
Thanks
Ok I found it!
StorageFile storageFile =
await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(uri);
var randomAccessStream = await storageFile.OpenReadAsync();
Stream stream = randomAccessStream.AsStreamForRead();
You can also do it in one less line:
Stream stream = await storageFile.OpenStreamForReadAsync();

Categories