How do I save an image I captured directly to sqlite(sqlite-net-pcl) database?
Here's my code but it can only save in "Internal Storage" of the phone.
private async void TakePhotoButton_Clicked(object sender, EventArgs e)
{
try
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("No Camera", "No Camera Available", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(
new StoreCameraMediaOptions
{
SaveToAlbum = true,
//Directory = "Sample",
//Name = "Test.jpg"
});
if (file == null)
return;
PathLabel.Text = file.AlbumPath;
MainImage.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
}
catch (Exception ex)
{
await DisplayAlert("error", ex.ToString(), "OK");
}
}
You should convert the stream to Byte array so that you save them to sqlite.
public byte[] GetImageStreamAsBytes(Stream input)
{
var buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
var imgDate = GetImageStreamAsBytes(file.GetStream());
Concerning the SaveToAlbum option
This will restult in 2 photos being saved for the photo. One in your private folder and one in a public directory that is shown. The value will be returned at AlbumPath. (Source)
Unless you really need the photo in the camera roll, there is no need to use SaveToAlbum.
Anyway, the file is still saved to your apps sandbox (the part of the devices storage that is reserved exclusively for your app) and you can retrieve the path with file.Path.
Having said that, it's easy to obtain the binary data representing your image
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("No Camera", "No Camera Available", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions());
if (file == null)
return;
var imageData = File.ReadAllBytes(file.Path);
Obviously (see here and here) it's possibly to store binary data (BLOBs) in an SQLite database. The simplest conceivable model to store an image in the database would be something like
class Image
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public byte[] Data { get; set; }
}
Assuming that _imageRepository is your repository abstraction you are saving the images in, the data could be saved as
// ...
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions());
if (file == null)
return;
var imageData = File.ReadAllBytes(file.Path);
_imageRepository.Add(new Image()
{
Data = imageData
});
Later on, to display the image, you can get the Image from the repository and use the Data property, e.g. by passing it to a MemoryStream (if you need a stream)
// example: Loading by ID, loading all images is conceivable, too
var image = _imageRepository.LoadImage(id);
ImageControl.ImageSource = ImageSource.FromStream(() => new MemoryStream(image.Data));
Related
I use a photo picker to let the user choose a profile pic and then I want to display the pic inside of an Image view. the following code works fine for me but then I would like to convert the image to a byte array, once I call the method to convert it the image doesn't display after the user picks it. I also tried to call the method outside the try and catch but no luck.
the image is getting selected (it's getting saved in local storage) so the only issue is that for some reason it doesn't display it in the view.
async void showMediaPicker()
{
var res = await MediaPicker.PickPhotoAsync();
try
{
var stream = await res.OpenReadAsync();
var finalImage = ImageSource.FromStream(() => stream);
myImage.Source = finalImage;
imgBytes = ImageSourceToBytes(finalImage);
}
catch (Exception e)
{
Console.Write("error" + e.Message) ;
}
}
If you want to convert the photo to Byte array , you could use the plugin Media.Plugin from Nuget to pick or take photo . Don't forget to add the relevant permissions on android and iOS platform .
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
DisplayAlert("No Camera", ":( No camera available.", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
SaveToAlbum = true,
Name = "test.jpg"
});
if (file == null)
return;
Then convert it to Stream firstly before convert it to byte array .
Stream stream = file.GetStream();
public byte[] GetImageStreamAsBytes(Stream input)
{
var buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
Now you can get the byte array like following
var imgDate = GetImageStreamAsBytes(stream);
if (!CrossMedia.Current.IsPickPhotoSupported)
{
await Application.Current.MainPage.DisplayAlert("Photos Not Supported", ":( Permission not granted to photos.", "OK");
return;
}
var file = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
{
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium,
});
if (file == null)
return;
var tmpSrc = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
ImageSource toBeConverted = tmpSrc;
I want the variable toBeConverted to be converted into Byte[] so
that I can send it to my webapi ...
ImageSource is a way to provide a source image for Xamarin.Forms.Image to show some content. If you're already showing something on the screen your Image view was populated with data that came from elsewhere, such as a file or resource or stored in an array in memory... or however else you got that in the first place. Instead of trying to get that data back from ImageSource you can keep a reference to it and upload it as needed.
So you could get the byte array from the file after you pick the photo.
var file = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
{
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium,
});
if (file == null)
return;
var bytes = File.ReadAllBytes(file.Path); // you could get the byte[] here from the file path.
This code also worked for me ...
private async void Capture()
{
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await Application.Current.MainPage.DisplayAlert("No Camera", ":( No camera available.", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
Directory = "Test",
SaveToAlbum = true,
CompressionQuality = 75,
CustomPhotoSize = 50,
PhotoSize = PhotoSize.Medium,
DefaultCamera = CameraDevice.Front
});
if (file == null)
return;
var stream = file.GetStream();
if (stream != null)
{
var StreamByte = ReadAllBytes(stream);
var NewStream = new MemoryStream(StreamByte);
// stream = mystream;
Device.BeginInvokeOnMainThread(() => {
ImageSource = ImageSource.FromStream(() => NewStream);
});
student.ProfilePicture = StreamByte;
}
}
public byte[] ReadAllBytes(Stream instream)
{
if (instream is MemoryStream)
return ((MemoryStream)instream).ToArray();
using (var memoryStream = new MemoryStream())
{
instream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
Is there a way to save the picture in an image control to the Android gallery in Xamarin Forms? All help is appreciated.
var image = new Image();
image.Source = "test.png";
Screenshot
You could use Dependency Service to get the stream from Resource/drawable image.
Create the IDependency interface.
public interface IDependency
{
MemoryStream DrawableByNameToByteArray(string fileName);
}
Android implementation
public class DependencyImplementation : IDependency
{
public MemoryStream DrawableByNameToByteArray(string fileName)
{
var context = Application.Context;
using (var drawable = Xamarin.Forms.Platform.Android.ResourceManager.GetDrawable(context, fileName))
using (var bitmap = ((BitmapDrawable)drawable).Bitmap)
{
var stream = new MemoryStream();
bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
bitmap.Recycle();
return stream;
}
}
}
For the IOS implementation, you could refer to the thread in SO.
Convert Image (from drawable folder) to ByteArray
And then register in Android Mainactivity.
DependencyService.Register<IDependency, DependencyImplementation>();
If your android version is highter than android 6.0, you need runtime permission for storage in this question. Please check the Plugin.Permissions with runtime permission.
https://github.com/jamesmontemagno/PermissionsPlugin
After that, you could save the image to picture internal storage.
var filename = "";
var source = image.Source as FileImageSource;
if (source != null)
{
filename = source.File;
}
var savingFile = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures);
//var savingFile1 = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "save.png");
var S = DependencyService.Get<IDependency>().DrawableByNameToByteArray(filename);
if (!File.Exists(savingFile))
{
File.WriteAllBytes(savingFile, S.ToArray());
}
In Internal Storage, you couldn't see the files without root permission.
If you want to view it, you could use adb tool.
Please check the way in link.
How to write the username in a local txt file when login success and check on file for next login?
You can use Media Plugin and it can solve your issue. https://github.com/jamesmontemagno/MediaPlugin
You can visit the above link.
takePhoto.Clicked += async (sender, args) =>
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
DisplayAlert("No Camera", ":( No camera available.", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
Directory = "Sample",
Name = "test.jpg"
});
if (file == null)
return;
await DisplayAlert("File Location", file.Path, "OK");
image.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
return stream;
});
};
I have a problem with views, when I open the camera and take the photo it sends me directly to the home screen, the same happens with an image of the gallery, this only happens with ios, since Android works without any problem. I would like to know what is happening because I have not found the error.
public async Task TakePicture()
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await Application.Current.MainPage.DisplayAlert("Error", "No se encontro una cámara disponible.", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
SaveToAlbum = false,
CompressionQuality = 30,
PhotoSize = PhotoSize.Small
});
if (file == null)
{
return;
}
byte[] array;
using (var memoryStream = new MemoryStream())
{
int count = 0;
file.GetStream().CopyTo(memoryStream);
array = memoryStream.ToArray();
while (count < 10)
{
if (array.Count() == 0)
{
file.GetStream().CopyTo(memoryStream);
array = memoryStream.ToArray();
await Task.Delay(1000);
}
count++;
}
}
Stream stream = new MemoryStream(array);
var image = ImageSource.FromStream(() => stream);
FotoPerfil = image;
User.Foto = array;
}
This part is to take the photo directly from the cell phone, I have no problem opening the camera, the error comes when I choose that photo sends me to another screen, I do not know if it is closing and reopening the application but I do not know what happens.
public async Task PickPicture()
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsPickPhotoSupported)
{
await Application.Current.MainPage.DisplayAlert("Error",
"No se otorgaron permisos para accesar a las fotos.", "OK");
return;
}
var file = await CrossMedia.Current.PickPhotoAsync(new PickMediaOptions
{
PhotoSize = PhotoSize.Medium,
CompressionQuality = 30,
});
if (file == null)
{
return;
}
byte[] array;
using (var memoryStream = new MemoryStream())
{
int count = 0;
file.GetStream().CopyTo(memoryStream);
array = memoryStream.ToArray();
while (count < 10)
{
if (array.Count() == 0)
{
file.GetStream().CopyTo(memoryStream);
array = memoryStream.ToArray();
await Task.Delay(1000);
}
count++;
}
}
await Application.Current.MainPage.DisplayAlert("Error", array.ToString(), "OK");
User.Foto = array;
Stream stream = new MemoryStream(array);
var image = ImageSource.FromStream(() => stream);
FotoPerfil = image;
}
The latter is to select any image from the gallery, and exactly the same happens.
am trying to show image directly from zip file without extracting file.
ZipArchiveEntry tumbnail = archive.Entries.Where(s => s.FullName.Equals("coverthumb.png")).FirstOrDefault();
if (tumbnail != null)
{
Stream picdata = tumbnail.Open();
await picdata.CopyToAsync (picdata);
BitmapImage bt = new BitmapImage();
bt.SetSource (picdata.AsRandomAccessStream());
image.Source = bt;
}
but getting an
cannot seek exception
please help.
the following code works for images but when tries to read many images at once some times it gives invalid data exception at bitmap creation
async public static Task<BitmapImage> GetImageFromZipEntry(ZipArchiveEntry zipentry)
{
//for extracting image inside a zip as bitmapimage
BitmapImage tm = new BitmapImage();
try {
if (zipentry != null)
{
using (Stream imstream = zipentry.Open())
{
using (MemoryStream immemorystream = new MemoryStream((int)zipentry.Length))
{
await imstream.CopyToAsync(immemorystream);
using (var sourceStream = new MemoryStream(immemorystream.ToArray()))
{
await tm.SetSourceAsync(sourceStream.AsRandomAccessStream());
}
}
}
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
tm = null;
}
return tm;
}