Xamarin Forms Labs Camera - Permanently Saving Images and Calling Them - xamarin.forms

I got the camera function to work and it displays the image on the page like i asked it too. But is there a way to permanently save the image on your phone or somewhere else and then call it?
Thank you so much

Here's some code that works for me.
IFileAccess is my wrapper around System.IO.File functions such as file open, write, check if exsists. If you're making your own file service look up Xamarin.Forms.Labs.Resolver and how to use it; if you're using shared Forms project type you can access System.IO.File directly from the Forms project. Assuming that's clear, the following
var fileAccess = Resolver.Resolve<IFileAccess> ();
mediaPicker.SelectPhotoAsync (new CameraMediaStorageOptions{ MaxPixelDimension = 1024 })
.ContinueWith(t=>{
if (!t.IsFaulted && !t.IsCanceled) {
var mediaFile = t.Result;
var fileAccess = Resolver.Resolve<IFileAccess> ();
string imageName = "IMG_" + DateTime.Now.ToString ("yy-MM-dd_HH-mm-ss") + ".jpg";
// save the media stream to a file
fileAccess.WriteStream (imageName, mediaFile.Source);
// use the stored file for ImageSource
ImageSource imgSource = ImageSource.FromFile (fileAccess.FullPath (imageName));
imgInXAML.Source = imgSource;
}
});
Further detail on IFileAccess.
In your Forms project create an interface like this:
public interface IFileAccess
{
bool Exists (string filename);
string FullPath(string filename);
void WriteStream (string filename, Stream streamIn);
}
In your iOS or Android or Shared project add a class FileAccess that implements IFileAccess:
public class FileAccess : IFileAccess
{
public bool Exists (string filename)
{
var filePath = GetFilePath (filename);
if (File.Exists (filePath)) {
FileInfo finf = new FileInfo (filePath);
return finf.Length > 0;
} else
return false;
}
public string FullPath (string filename)
{
var filePath = GetFilePath (filename);
return filePath;
}
static string GetFilePath (string filename)
{
var documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
var filePath = Path.Combine (documentsPath, filename);
return filePath;
}
public void WriteStream (string filename, Stream streamIn)
{
var filePath = GetFilePath (filename);
using (var fs = File.Create (filePath)) {
streamIn.CopyTo (fs);
}
}
}
If you're already using Xamarin.Forms.Labs.Resolver then add only the line to register the service, otherwise in your iOS or Android project find a call to Forms.Init() and right before it add
var resolverContainer = new SimpleContainer ();
resolverContainer.Register<IFileAccess> (t => new FileAccess ()); // maybe just this line
Resolver.SetResolver (resolverContainer.GetResolver ());

Related

ASP.NET Core Image Resize

I'm trying to resize imagine with System.Drawing but im taking that file as IFormFile and when i use the System.Drawing its just keep warning me to about that : cannot implicitly convert type 'System.Drawing.Bitmap' to 'Microsoft.AspNetCore.Http.IFormFile' . I need to resize those photos and save them as IFormFile but i dont know how to do that.
public Task<IFormFile> ResizeImagine300x300(IFormFile file)
{
Image image = Image.FromStream(file.OpenReadStream(), true, true);
var newImage = new Bitmap(1024, 768);
using (var g = Graphics.FromImage(newImage))
{
g.DrawImage(image, 0, 0, 1024, 768);
};
return newImage;//the point where i get the error
}
Is it possible to do it in my way?
If its not possible, then which way i should follow?
Thanks for any suggestion
Edit: I wanna return as a IFormFile because i have a method which is uploading those files to my database. here is my method :
public async Task<FileRepo> FileUploadToDatabase(List<IFormFile> files)
{
foreach (var file in files)
{
var fileName = Path.GetFileNameWithoutExtension(file.FileName);
var fileExtension = Path.GetExtension(file.FileName);
_fileRepo = new FileRepo
{
FileName = fileName,
FileExtension = fileExtension,
FileType = file.ContentType,
CreatedDate= DateTime.Now
};
using (var dataStream = new MemoryStream())
{
await file.CopyToAsync(dataStream);
_fileRepo.FileData = dataStream.ToArray();
}
}
return _fileRepo;
}
After that I'm uploading that _fileRepo variable to my database like that :
var File = _fileUploader.FileUploadToDatabase(files);
var FileResult = File.Result;
FileResult.ProductID = ProductID;
_unitOfWorkFR.RepositoryFileRepo.Create(FileResult);

Xamarin.Forms Directory Picker

I would like to save a file in a user selected folder, thats why I would like to provide a directory list to user and user will be able to choose the directory where he wants to export the data. Unfortuntely I could not find any example for directory/folder picker, I just found a file picker which is not useful for me..
https://github.com/jfversluis/FilePicker-Plugin-for-Xamarin-and-Windows
Is there any component for picking a folder for Xamarin.Forms? Actually I am just doing for Android but we use Xamarin.forms
There is none I can think of.
With netstandard everything is way more simple as you can use the classic c# File api to get the folders.
You just have to know the mappings between special folders and android folders (per example):
System.Environment.SpecialFolder Path
ApplicationData INTERNAL_STORAGE/.config
Desktop INTERNAL_STORAGE/Desktop
LocalApplicationData INTERNAL_STORAGE/.local/share
MyDocuments INTERNAL_STORAGE
MyMusic INTERNAL_STORAGE/Music
MyPictures INTERNAL_STORAGE/Pictures
MyVideos INTERNAL_STORAGE/Videos
Personal INTERNAL_STORAGE
source: https://learn.microsoft.com/en-US/xamarin/android/platform/files/
same for ios:
https://learn.microsoft.com/en-US/xamarin/ios/app-fundamentals/file-system
But it's really easy to implement, just enumerate all folders and display them in a ListView.
EDIT: more details on implementation.
In fact you want to code a "directory explorer", it's easy, here is the concept.
You have a ListView in your Page
You have a Cancel button and a Select button in your Page
You have a CurrentPath in your ViewModel
You bind CurrentPath to the Title of your Page
You have an List<DirectoryViewModel> Directories in your ViewModel
Each time a user click on a item from the list:
You add the directory name in your current path
You get all the directories from the new path, and update your Directories property (don't forget RaisePropertyChange(nameof(Directories)))
The ListView will be updated accordingly
Each time you back:
You remove last part of your current path
same as before
If you arrive to root path "/", you do nothing when clicking on back.
Oh and you could use this Grid component to instead of the ListView, will be nicer ;)
https://github.com/roubachof/Sharpnado.Presentation.Forms#grid-Layout
You can edit this to make it work..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Java.IO;
using Java.Util;
namespace Android.Basic.IO
{
public class DirectoryPicker : ListActivity
{
public const String START_DIR = "startDir";
public const String ONLY_DIRS = "onlyDirs";
public const String SHOW_HIDDEN = "showHidden";
public const String CHOSEN_DIRECTORY = "chosenDir";
public const int PICK_DIRECTORY = 43522;
private File dir;
private Boolean showHidden = false;
private bool onlyDirs = true;
public override void OnCreate(Bundle savedInstanceState, PersistableBundle persistentState)
{
base.OnCreate(savedInstanceState, persistentState);
Bundle extras = Intent.Extras;
dir = OS.Environment.ExternalStorageDirectory;
if (extras != null)
{
String preferredStartDir = extras.GetString(START_DIR);
showHidden = extras.GetBoolean(SHOW_HIDDEN, false);
onlyDirs = extras.GetBoolean(ONLY_DIRS, true);
if (preferredStartDir != null)
{
File startDir = new File(preferredStartDir);
if (startDir.IsDirectory)
{
dir = startDir;
}
}
}
SetContentView(Resource.Layout.folder_chooser_activity);
var title = dir.AbsolutePath.ToString();
Title = (title);
Button btnChoose = (Button)FindViewById(Resource.Id.btnChoose);
String name = dir.Name;
if (name.Length == 0)
name = "/";
btnChoose.Text = ("Choose " + "'" + name + "'");
btnChoose.Click += delegate
{
returnDir(dir.AbsolutePath);
};
ListView lv = this.ListView;
lv.TextFilterEnabled = (true);
if (!dir.CanRead())
{
Context context = ApplicationContext;
String msg = "Could not read folder contents.";
Toast.MakeText(context, msg, ToastLength.Long).Show();
return;
}
var files = filter(dir.ListFiles(), onlyDirs, showHidden);
String[] names = Names(files);
ListAdapter = (new ArrayAdapter<String>(this, Resource.Layout.folder_chooser_item, names));
lv.ItemClick += (ff, gg) =>
{
var position = gg.Position;
if (!files[gg.Position].IsDirectory)
return;
String path = files[position].AbsolutePath;
var intent = new Intent(this, typeof(DirectoryPicker));
intent.PutExtra(DirectoryPicker.START_DIR, path);
intent.PutExtra(DirectoryPicker.SHOW_HIDDEN, showHidden);
intent.PutExtra(DirectoryPicker.ONLY_DIRS, onlyDirs);
StartActivityForResult(intent, PICK_DIRECTORY);
};
}
protected void OnActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == PICK_DIRECTORY && resultCode == (int)Result.Ok)
{
Bundle extras = data.Extras;
String path = (String)extras.Get(DirectoryPicker.CHOSEN_DIRECTORY);
returnDir(path);
}
}
private void returnDir(String path)
{
Intent result = new Intent();
result.PutExtra(CHOSEN_DIRECTORY, path);
SetResult(Result.Ok, result);
Finish();
}
public List<File> filter(File[] file_list, bool onlyDirs, bool showHidden)
{
var files = new List<File>();
foreach (var file in file_list)
{
if (onlyDirs && !file.IsDirectory)
continue;
if (!showHidden && file.IsHidden)
continue;
files.Add(file);
}
Collections.Sort(files);
return files;
}
public String[] Names(List<File> files)
{
String[] names = new String[files.Count];
int i = 0;
foreach (var file in files)
{
names[i] = file.Name;
i++;
}
return names;
}
}
}
Start activity as result then catch in OnActivityResult
if (requestCode == DirectoryPicker.PICK_DIRECTORY && resultCode == Result.Ok)
{
Bundle extras = data.Extras;
String path = (String)extras.Get(DirectoryPicker.CHOSEN_DIRECTORY);
// do stuff with path
}

How to save an image in SQLite in Xamarin Forms?

I have the following two methods that handles taking photos from a camera and picking photos from a library. They're both similar methods as at the end of each method, I get an ImageSource back from the Stream and I pass it onto another page which has an ImageSource binding ready to be set. These two method work perfectly. The next step now is to save the Image in SQLite so I can show the images in a ListView later on. My question for the XamGods (Xamarin Pros =), what is the best way to save image in SQLite in 2019? I have been in the forums for hours and I still don't have a tunnel vision on what I want to do. I can either
Convert Stream into an array of bytes to save in Sqlite.
Convert ImageSource into an array of bytes (messy/buggy).
Somehow retrieve the actual Image selected/taken and convert that into an array of bytes into SQLite
I'm sorry if my question is general, but Xamarin does not provide a clear-cut solution on how to save images in SQLite and you can only find bits and pieces of solutions throughout the forums listed below.
How to save and retrieve Image from Sqlite
Load Image from byte[] array.
Creating a byte array from a stream
Thank you in advance!
private async Task OnAddPhotoFromCameraSelected()
{
Console.WriteLine("OnAddPhotoFromCameraSelected");
var photo = await Plugin.Media.CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions() { });
var stream = photo.GetStream();
photo.Dispose();
if (stream != null)
{
ImageSource cameraPhotoImage = ImageSource.FromStream(() => stream);
var parms = new NavigationParameters();
parms.Add("image", cameraPhotoImage);
var result = await NavigationService.NavigateAsync("/AddInspectionPhotoPage?", parameters: parms);
if (!result.Success)
{
throw result.Exception;
}
}
}
private async Task OnAddPhotoFromLibrarySelected()
{
Console.WriteLine("OnAddPhotoFromLibrarySelected");
Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
if (stream != null)
{
ImageSource selectedImage = ImageSource.FromStream(() => stream);
var parms = new NavigationParameters();
parms.Add("image", selectedImage);
parms.Add("stream", stream);
var result = await NavigationService.NavigateAsync("/AddInspectionPhotoPage?", parameters: parms);
if (!result.Success)
{
throw result.Exception;
}
}
}
As Jason said that you can save image path into sqlite database, but if you still want to save byte[] into sqlite database, you need to convert stream into byte[] firstly:
private byte[] GetImageBytes(Stream stream)
{
byte[] ImageBytes;
using (var memoryStream = new System.IO.MemoryStream())
{
stream.CopyTo(memoryStream);
ImageBytes = memoryStream.ToArray();
}
return ImageBytes;
}
Then load byte[] from sqlite, converting into stream.
public Stream BytesToStream(byte[] bytes)
{
Stream stream = new MemoryStream(bytes);
return stream;
}
For simple sample, you can take a look:
Insert byte[] in sqlite:
private void insertdata()
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "sqlite1.db3");
using (var con = new SQLiteConnection(path))
{
Image image = new Image();
image.Content = ConvertStreamtoByte();
var result = con.Insert(image);
sl.Children.Add(new Label() { Text = result > 0 ? "insert successful insert" : "fail insert" });
}
}
Loading image from sqlite:
private void getdata()
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "sqlite1.db3");
using (var con = new SQLiteConnection(path))
{
var image= con.Query<Image>("SELECT content FROM Image ;").FirstOrDefault();
if(image!=null)
{
byte[] b = image.Content;
Stream ms = new MemoryStream(b);
image1.Source = ImageSource.FromStream(() => ms);
}
}
}
Model:
public class Image
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string FileName { get; set; }
public byte[] Content { get; set; }
}

Get FileStream from form posted file

I have a control on view page. When user selects the file and clicks on submit button this makes ajax call to upload the file on server. Unfortunately my server method accepts file path (like C:/Videos/1.mp4) to upload. This works great with string demoPath in the code below but I'm not sure how to get similar path when user selects in control. Due to sercurity reasons modern browsers not allows exposing paths. How to achieve this?
[HttpPost]
public async Task<JsonResult> Upload(string lectureId, string filepath)
{
for (int i = 0; i < Request.Files.Count; i++)
{
//// This works great
//string demoPath = "C:/Users/abchi/Desktop/BigBuckBunny.mp4";
var file = Request.Files[i];
var fileName = Path.GetFileName(file.FileName);
//var path = Path.Combine(Server.MapPath("~/User/"), fileName);
//file.SaveAs(path);
//await RunUploader(demoPath);
await RunUploader(get_path_from_posted_file_or_request);
}
return Json(new { error = false, message = "Video uploaded." });
}
public async Task RunUploader(string filePath)
{
// :::::::
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
// ::::
}
// ::::::
}
I'm not sure this is expected because I did not quite understand.
Download the file path of the user's computer can not be - https://stackoverflow.com/a/15201258/4599089
but if you want to have access to the FileStream on your server:
File has InputStream and you can use this:
[HttpPost]
public async Task<JsonResult> Upload(string lectureId, string filepath)
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/User/"), fileName);
var fileStream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
file.InputStream.CopyTo(fileStream);
fileStream.Close();
await RunUploader(path); //path or stream
}
return Json(new { error = false, message = "Video uploaded." });
}
public async Task RunUploader(string filePath)
{
// :::::::
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
// ::::
}
// ::::::
}
I asked my fellow dev to make necessary changes in public async Task RunUploader(string filePath) parameters. Said code was part of YouTube .NET samples for console apps. Now we are developing for web, in this case we can't pass full path. So they made following changes:
[HttpPost]
public async Task<JsonResult> Upload(string lectureId)
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
Stream fileStream = file.InputStream;
await Run(fileStream);
}
return Json(new { error = false, message = "Video uploaded." });
}
public async Task Run(Stream fileStream)
{
// ::::::::::
using (fileStream)
{
// ::::::
}
// ::::::::::
}
Now with this change everything started working.

Read text file with PCLStorage in Xamarin.Forms

Is there a way to read an embedded JSON file in a PCL project with PCLStorage module? I looked everywhere but couldn't find a sample related to this matter.
EDIT: PCLStorage link: https://github.com/dsplaisted/pclstorage
you need something like this:
public static async Task<string> ReadFileContent(string fileName, IFolder rootFolder)
{
ExistenceCheckResult exist = await rootFolder.CheckExistsAsync(fileName);
string text = null;
if (exist == ExistenceCheckResult.FileExists)
{
IFile file = await rootFolder.GetFileAsync(fileName);
text = await file.ReadAllTextAsync();
}
return text;
}
to use:
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder myCoolFolder = await rootFolder.CreateFolderAsync("MyCoolForler", CreationCollisionOption.OpenIfExists);
string fileContent = await this.ReadFileContent("MyCoolFile.txt", myCoolFolder);
You should be able to read embedded resources like this:
var assembly = typeof(LoadResourceText).GetTypeInfo().Assembly;
Stream stream = assembly.GetManifestResourceStream("WorkingWithFiles.PCLTextResource.txt");
string text = "";
using (var reader = new System.IO.StreamReader (stream)) {
text = reader.ReadToEnd ();
}
Xamarin has a great guide on how to work with them here.
https://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/files/

Resources