Hello,
I am currently working on an application where all data created within the application MUST be encrypted. We have started using this library as a quick way to get photo taking and picking from a library up and running and it works very well!
What I would like to ask is, is there a way to capture photos and possibly videos from the camera without saving the MediaFile to disk? If not is there any plan to do so in the future?
Failing that as I appreciate a large video can potentially cause issues, would it be possible to provide a custom file writer implementation that can handle writing the image/video out to an encrypted stream?
Thanks
Shaun
I have the same problem. My app should take photos and send them to server. The photos are sensitive information, so its strongly prohibited to have them to be saved on the device. With the new feature that photos can be saved to camera roll/gallery even thought i explicitly set SaveToAlbum = false, its still being saved to DCIM folder. I could delete it from there, but I cannot even know what is the name is given to it.
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
SaveToAlbum = false
});
Is it possible to prevent it from being saved to DCIM?
This is happening for me as well @marikatz, and am in a similar situation with sensitive information/only needing it to go to the server. I have the same code with SaveToAlbum set to false as well, with CompressionQuality set to 92 and AllowCropping set to true as my other StoreCameraMediaOptions.
@marikatz and @tylerseawell4 you can supply a Path property to the StoreCamerMediaOptions class. I understand that this would allow you to store the file somewhere other than DCIM.
@bijington so when I have the SaveToAlbum = false in StoreCameraMediaOptions, it saves it to my camera roll with .AlbumPath and Path being the same ("/storage/emulated/0/Android/data/MY_PACKAGE_NAME/files/Pictures/MY_IMAGE.jpg"); however if I put SaveToAlbum = true, the .Path string stays the same as above, but the .AlbumPath gets set to ("/storage/emulated/0/Pictures/temp/MY_IMAGE.jpg"). So it looks like it defaults to a 'temp' folder. In this case, the image gets put in BOTH my camera roll and that temp folder.
Also, if I have SaveToAlbum = true, and set .Directory in the StoreCameraMediaOptions to "test", the .Path is changed to ("/storage/emulated/0/Android/data/MY_PACKAGE_NAME/files/Pictures/test/MY_IMAGE.jpg), and the .AlbumPath is ("/storage/emulated/0/Pictures/test/MY_IMAGE.jpg"). A 'test' folder is created in my gallery and the image is stored in both that folder AND the camera roll.
Furthermore, I created a simple Dependency Service for my Droid project that deleted the image with both System.IO.File.Delete(string path), and the File.delete from the Java.IO and the file would throw an error if the .Path was passed in and SaveToAlbum was set to false ("Could Not Find File: /storage/emulated/0/Android/data/MY_PACKAGE_NAME/files/Pictures/MY_IMAGE.jpg"), but would work if SaveToAlbum = true and the .AlbumPath was passed in; however, the photo would still be present in my camera roll.
@tylerseawell4 that does sound like there is an issue around there. What version of the plugin are you using? I am using version v3.1.3 and I am not seeing any images make it on to my camera roll. This is my code:
private async Task<MediaItem> AddFromCameraAsync()
{
await CrossMedia.Current.Initialize();
var hasPermission = await this.CheckForPermissionsAsync();
if (!hasPermission)
{
await Application.Current.MainPage.DisplayAlert(AppResources.PermissionsDenied, AppResources.PermissionsDeniedMessage, AppResources.OK);
return null;
}
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
//DisplayAlert("No Camera", ":( No camera available.", "OK");
return null;
}
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
Directory = "Sample",
Name = "image.jpg",
RotateImage = true
});
if (file == null)
{
return null;
}
//await DisplayAlert("File Location", file.Path, "OK");
return await this.GenerateMediaItem(MediaTypes.Image, file.GetStream(), file.Path);
}
@bijington I am on the same version as you. Thanks for the code. My implementation is similar, but I'll give yours a try and see if anything comes about from it!
@jamesmontemagno does the closure of this item mean that it is a no to my original question?
I'm doing the following to retrieve a photo without saving to disk first.
/// <summary>
/// Returns a Photo taken from Device Camera.
/// </summary>
public class CameraHelper
{
/// <summary>
/// Returns Photo Bytes.
/// </summary>
/// <returns>Photo Byte Array.</returns>
public static async Task<byte[]> GetPhotoBytes()
{
var photo = await GetPhotoStream();
return photo?.ToByteArray();
}
/// <summary>
/// Photo Stream.
/// </summary>
/// <returns>Photo Stream.</returns>
public static async Task<Stream> GetPhotoStream()
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await Application.Current.MainPage.DisplayAlert("No Camera", "No camera available.", "OK");
return null;
}
var cameraStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
if (cameraStatus != PermissionStatus.Granted)
{
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Permission.Camera });
cameraStatus = results[Permission.Camera];
}
if (cameraStatus == PermissionStatus.Granted)
{
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions
{
RotateImage = true,
AllowCropping = true,
Name = "photo.jpg",
SaveToAlbum = false,
DefaultCamera = CameraDevice.Rear
});
return file?.GetStreamWithImageRotatedForExternalStorage();
}
else
{
await Application.Current.MainPage.DisplayAlert("Permissions Denied", "Unable to take photos.", "OK");
if (Device.RuntimePlatform == Device.iOS)
{
var result = await Application.Current.MainPage.DisplayAlert("Settings", "Update camera permissions?", "Yes", "No");
if (result)
{
CrossPermissions.Current.OpenAppSettings();
}
}
}
return null;
}
}
@aherrick thanks for the sample. Forgive me if I am wrong though but I do believe that the code inside TakePhotoAsync still saves the photo to disk. It is that operation that I ultimately want to avoid.
There is no option not to save it to disk at all, you can simply delete the file after if needed.
@jamesmontemagno thanks for the confirmation. Would there be any consideration towards a feature request allowing someone to override how the file is written? Essentially I have a SecureArchiveWriter that would take the place of the existing file writing and can even support streaming in so videos should also fit this model. I am hoping to find a solution without having to fork your code but I am expecting that is probably where I will end up.
@bijington seems like a reasonable request to have an option not to save to disk. Frankly I thought SaveToAlbum = false is what did that.
For me I have no need to save to disk... so this would be nice. I simply want to capture a photo in memory and post to my API.
@jamesmontemagno I tried deleting and the method is returning true and that the file has been deleted. But, right after that, I am using the same method to check if the file is there and it's returning true and I can see it in the file system.
I already posted a question about it on StackOverflow Here
But, I want to know, what are the consequences of leaving the files? will the app get too big after sometime and affect the system?
BTW: Thanks for the great Plugin.
@aherrick
I fixed the issue. It was a typo, I was calling the "FileExists" instead of "DeleteFile" in the helper class.
Now, I can delete the images after taking them from the camera.
The code is in the StackOverflow and I can give more descriptions or upload the code if anyone needs it.
@ali-h2010 Cool. Is the only solution to physically delete the file? There is no way for the plugin platform to simply not save the file to disk in the first place?
As a heads up in my .NET Standard 2.0 Forms project I was able to just delete the file.
System.IO.File.Delete(file.Path);
I then confirmed the file no longer exists. Works just fine!
Most helpful comment
As a heads up in my .NET Standard 2.0 Forms project I was able to just delete the file.
System.IO.File.Delete(file.Path);I then confirmed the file no longer exists. Works just fine!