I am having issues with some sample code that I am using from one of Nokia's Imaging SDK samples they provide. Essentially I am trying to save an image to IsolatedStorage. The code that I am reusing has been used successfully elsewhere in the solution, but when I try to use it there are no errors but it does not proceed with the following statements. Essentially in the StorePhoto method, once IBuffer buffer = await App.PhotoModel.RenderFullBufferAsync(); is called no error occurs but no code below that which is actually performs the save to isolated storage operation is ran, so no image is ever saved.
SavePage.xaml.cs
private static string _photoModelPath = #"\Lockscreen\v1\PhotoModel";
private static string _photoModelBufferFilename = #"buffer.data";
public async static void StorePhoto()
{
string _photoModelPath = #"\Lockscreen\v1\LockScreen";
string _photoModelBufferFilename = #"buffer.data";
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!storage.DirectoryExists(_photoModelPath))
{
storage.CreateDirectory(_photoModelPath);
}
if (storage.FileExists(_photoModelPath + #"\" + _photoModelBufferFilename))
{
storage.DeleteFile(_photoModelPath + #"\" + _photoModelBufferFilename);
}
IBuffer buffer = await App.PhotoModel.RenderFullBufferAsync(); //code exiting method with no error
if (buffer != null)
{
IsolatedStorageFileStream originalFile = storage.CreateFile(_photoModelPath + #"\" + _photoModelBufferFilename);
Stream bufferStream = buffer.AsStream();
bufferStream.CopyTo(originalFile);
bufferStream.Flush();
bufferStream.Close();
bufferStream.Dispose();
originalFile.Flush();
originalFile.Close();
originalFile.Dispose();
}
}
}
MainPage.xaml.cs
private async void _saveItem_Click(object sender, EventArgs e)
{
Helpers.SaveHelper.StorePhoto(); //calling the StorePhoto method here
}
PhotoModel.cs (from Nokia Imaging SDK sample)
/// <summary>
/// Renders current image with applied filters to a buffer and returns it.
/// Meant to be used where the filtered image is for example going to be
/// saved to a file.
/// </summary>
/// <returns>Buffer containing the filtered image data</returns>
public async Task<IBuffer> RenderFullBufferAsync()
{
using (BufferImageSource source = new BufferImageSource(_buffer))
using (FilterEffect effect = new FilterEffect(source) { Filters = _components })
using (JpegRenderer renderer = new JpegRenderer(effect))
{
return await renderer.RenderAsync();
}
}
Turns out to solve this I had to put the code that saves the image into the same page that I was originally calling that method, and also it needs to be of type Task so that the async/await will work properly.
Related
I have image upload feature in my Xamarin.iOS application. I have stored this uploaded image(s) in firebase storage. My files gets uploaded to firebase storage successfully, but the issue is:
When I am trying to get all images using listAll() method of firebase
storage it not return all images until the folder have images >= 2.
Code to upload image on firebase storage:
private void ImagePicker_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
if (e.Info[UIImagePickerController.MediaType].ToString() == "public.image")
{
NSData imgData = new NSData();
imgData = e.OriginalImage.AsJPEG();
Guid uniqId = Guid.NewGuid(); // use - uniqId.ToString()
FirebaseClient.Instance.UploadAdventurePhoto(imgData, this.Adventure.Id, uniqId.ToString());
//(Path of folder - gs://myapp.appspot.com/adventures/00ac45a3-7c92-4335-a4b8-b9b705c4dd72)
StorageReference photsUrl = Storage.DefaultInstance.GetReferenceFromUrl($"gs://myapp.appspot.com/adventures/{this.Adventure.Id}");
photsUrl.ListAll(this.Handler);
}
this.imagePicker.DismissViewController(true, null);
}
// Add image to Firestore collection's Document.
private async void Handler(StorageListResult result, NSError arg2)
{
foreach (var image in result.Items)
{
// Logic to append image to Firestore document.
}
}
/// <param name="imgData">Selected image that needs to be stored on storage.</param>
/// <param name="adventureId">Name of the folder.</param>
/// <param name="imageName">By this name image will get stored in folder.</param>
public void UploadAdventurePhoto(NSData imgData, string adventureId, string imageName)
{
StorageReference adventurePictureRef = Storage.DefaultInstance.GetReferenceFromPath($"adventures/{adventureId}/{imageName}");
StorageMetadata metaData = new StorageMetadata();
metaData.ContentType = "image/jpg";
adventurePictureRef.PutData(imgData, metaData);
}
After first image get uploaded, image get uploaded successfully but when handler gets called it gives this response:
But after this when I upload 2nd image that time it give Firebase.Storage.StorageReference1 in response:
Means if there are two images then only it returns url reference. How to fix this issue?
I have already added rules_version = '2'; in storage rules.
Haven't got the solution for listAll() but I have got the work around for this problem.
What I was trying is to get all the images from firebase storage using listAll(), but in that I am getting this issue. So now instead of listAll() I am using PutData() method's completion handler.
The completion handler will provide you the Metadata of an uploaded image. From this meta data we can get image directly like this:
metadata.Name
Here is how I have fix the problem by adding completion handler in UploadAdventurePhoto() method:
private void UploadAdventurePhoto(NSData imgData, string folderName, string imageName)
{
StorageReference adventurePictureRef = Storage.DefaultInstance.GetReferenceFromPath($"adventures/{folderName}/{imageName}");
StorageMetadata metaData = new StorageMetadata();
metaData.ContentType = "image/jpeg";
adventurePictureRef.PutData(imgData, metaData, this.HandleStorageGetPutUpdateCompletion);
}
private async void HandleStorageGetPutUpdateCompletion(StorageMetadata metadata, NSError error)
{
if (error != null)
{
// Uh-oh, an error occurred!
return;
}
var url = metadata.Name;
var downloadUrl = metadata.Path;
Debug.WriteLine($"Image url - {url}\n Path-{downloadUrl}");
CollectionReference collectionRef = Firestore.SharedInstance.GetCollection(FirebaseClient.AdventuresCollection);
var docRef = collectionRef.GetDocument(this.Adventure.Id);
var keys = new NSString[]
{
new NSString($"{AdventureBase.PhotoPropName}"),
};
var value = new NSObject[]
{
new NSString(url),
};
var objects = new NSObject[]
{
FieldValue.FromArrayUnion(value),
};
var dict = new NSDictionary<NSString, NSObject>(keys, objects);
await docRef.SetDataAsync(dict, true);
docRef.AddSnapshotListener(this.UpdateDataHandler);
}
private async void UpdateDataHandler(DocumentSnapshot snapshot, NSError error)
{
if (error != null)
{
// something went wrong
Debug.WriteLine($"Error - {error.Description}");
return;
}
Toast.MakeToast("Image uploaded successfully").Show();
}
I am running into a problem with SQLite where I imported a database from the assets folder into forms, now I'm trying to load the database using the method given by Microsoft, but now when I am loading it shows the tablemappings don't exist.
I know this because if I set a breakpoint on the bit where it checks the tablemappings, all I'm given is the table mappings to the database file that is created in the app.
I once managed to get it to load one of the databases and it told me that it cannot write to a readonly database, so it was trying to create a new table (I honestly do not know how it happened or how to recreate it, because changed the code back and it doesnt reproduce)
The error actually thrown is a "NullRefrenceException: loading...", but I'm 90% sure I am getting this one since the initializer is an async task. Anyway I have polly to try and rerun the initializer after it should have fully initialized (A solution that works fantastically with my main database, that is created on device)
When I continue past it everything just freezes.
I used the profiler and after a bit it did save the model, it also show loads of nullrefrenceexceptions but basically it just ignores not loading the database.
Honestly I'm sitting here for hours trying different things, and can't seem to find a solution, I tried looking for a few hours as well online...
DataBase
private async Task Initialize()
{
_db = await GetDatabaseConnection<SabbathModel>();
}
static readonly Lazy<SQLiteAsyncConnection> _lazyInitializer = new Lazy<SQLiteAsyncConnection>(() =>
{
return new SQLiteAsyncConnection(Constants.InternalDatabasePath, Constants.InternalDBFlags);
});
static SQLiteAsyncConnection DB => _lazyInitializer.Value;
public static bool initialized = false;
protected static async Task<SQLiteAsyncConnection> GetDatabaseConnection<T>()
{
if (!initialized)
{
if (!DB.TableMappings.Any(x => x.MappedType == typeof(T)))
{
await DB.CreateTablesAsync(CreateFlags.None, typeof(T)).ConfigureAwait(false);
}
initialized = true;
}
return DB;
}
Constants Mentioned:
public static string InternalDatabasePath
{
get
{
var basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return Path.Combine(basePath, InternalDatabaseFilename);
}
}
public const SQLite.SQLiteOpenFlags InternalDBFlags =
// open the database in read/write mode
SQLite.SQLiteOpenFlags.ReadOnly |
// enable multi-threaded database access
SQLite.SQLiteOpenFlags.PrivateCache;
public const string InternalDatabaseFilename = "InternalDatabase.db3";
and the task that loads the database on the phone
private async Task CopyInternalDataBase()
{
string dbPath = Constants.InternalDatabasePath;
if (!File.Exists(dbPath))
{
using BinaryReader br = new BinaryReader(await FileSystem.OpenAppPackageFileAsync("InternalDataBase.db"));
using BinaryWriter bw = new BinaryWriter(new FileStream(dbPath, FileMode.Create));
byte[] buffer = new byte[2048];
int len = 0;
while ((len = br.Read(buffer, 0, buffer.Length)) > 0)
{
bw.Write(buffer, 0, len);
}
}
}
I'm trying to read and write data with json file.
I created some class.
public class SimpleTask{...}
public class DayTask{...}
public class DataModel
{
...
private async Task GetSimpleTaskAsync()
{
if (_daytask.Count != 0)
return;
string fileName = "a.json";
Uri appUri = new Uri("ms-appx:///"+ fileName);
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(appUri);
string jsonText = await FileIO.ReadTextAsync(file);
JsonObject jsonObject = JsonObject.Parse(jsonText);
JsonArray jsonArray = jsonObject["DayTasks"].GetArray();
foreach (JsonValue daytaskValue in jsonArray)
{
JsonObject daytaskObject = daytaskValue.GetObject();
ObservableCollection<SimpleTask> simpletask = new ObservableCollection<SimpleTask>();
foreach (JsonValue simpletaskValue in daytaskObject["Tasks"].GetArray())
{
JsonObject simpletaskObject = simpletaskValue.GetObject();
simpletask.Add(new SimpleTask( simpletaskObject["StartTime"].GetString(),
simpletaskObject["EndTime"].GetString(),
simpletaskObject["Description"].GetString()));
}
DayTask daytask = new DayTask(daytaskObject["Day"].GetString(),simpletask);
this.DayTasks.Add(daytask);
}
}
}
As you can see, i have a method that gets data form a.json file. I created a.json file:
In the MainPage.xaml.cs, there is a method which calls GetDayTaskAysnc() method and retrieves data :
private async void ReadData1(object sender, TappedRoutedEventArgs e)
{
string test = String.Empty;
var daytask = await DataModel.GetDayTaskAsync();
foreach (var tasks in daytask)
{
test += String.Format("Day:{0}:\n", tasks.Day);
foreach (var simpletask in tasks.Tasks)
{
test += String.Format("\tStart Time: {0}\n", simpletask.StartTime);
test += String.Format("\tEnd Time: {0}\n", simpletask.EndTime);
test += String.Format("\tDescription Time: {0}\n", simpletask.Description);
}
}
TextBlock.Text = test;
}
It worked fine ! But i want to write data to the same file, so i added data in hardcore way:
private List<DayTask> creatList()
{
List<DayTask> DayTasks = new List<DayTask>();
ObservableCollection<SimpleTask> simpletask1 = new ObservableCollection<SimpleTask>();
simpletask1.Add(new SimpleTask("6AM","7AM","Breakfast"));
simpletask1.Add(new SimpleTask("8AM", "9AM", "Game"));
ObservableCollection<SimpleTask> simpletask2 = new ObservableCollection<SimpleTask>();
simpletask2.Add(new SimpleTask("6AM", "7AM", "Sleep"));
simpletask2.Add(new SimpleTask("8AM", "9AM", "School"));
DayTasks.Add(new DayTask ("3/8/2014",simpletask1));
DayTasks.Add(new DayTask("4/8/2014", simpletask2));
return DayTasks;
}
private async void WriteData(object sender, TappedRoutedEventArgs e)
{
string json = "a.json";
List<DayTask> daytasks = creatList();
var serializer = new DataContractJsonSerializer(typeof(List<DayTask>));
var stream = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync(json, CreationCollisionOption.ReplaceExisting);
using (stream)
{
serializer.WriteObject(stream, daytasks);
}
TextBlock.Text = "Write to Json file succeeded";
}
When i ran my app with window phone emulator, Firstly, it wrote to the file. Then i clicked read data button to ensure data written correctly, the emulator showed data from a.json file without being modified by WriteData() method. I continued to creat the second read data method:
private async void ReadData2(object sender, TappedRoutedEventArgs e)
{
string test = String.Empty;
string json = "a.json";
string content = String.Empty;
List<DayTask> mytasks = new List<DayTask>();
var deserializer = new DataContractJsonSerializer(typeof(List<DayTask>));
var stream = await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync(json);
using (stream)
{
mytasks = (List<DayTask>)deserializer.ReadObject(stream);
}
foreach (var tasks in mytasks)
{
test += String.Format("Day:{0}:\n", tasks.Day);
foreach (var simpletask in tasks.Tasks)
{
test += String.Format("\tStart Time: {0}\n", simpletask.StartTime);
test += String.Format("\tEnd Time: {0}\n", simpletask.EndTime);
test += String.Format("\tDescription Time: {0}\n", simpletask.Description);
}
}
TextBlock.Text = test;
}
I deployed my app several times, and this is my result:
ReadData2() : 'System.IO.FileNotFoundException'
WriteData() -> ReadData1(): Data from a.json was shown
WriteData() -> ReadData2(): Data from creatList() was shown
WriteData() -> ReadData1(): Data from a.json was shown -> ReadData2(): Data from creatList() was shown
So that, i have some question:
Do i have 2 json files, one i created by adding into my project and the other one i created when ran WriteData() method ? What is their paths ?
If my file is data.json in DataSource folder, how can i write data to it ? I can read data from it using uri like GetSimpleTaskAsync() but i don't know how to write to it correctly. (I tried to convert object into string to write but can't read it again, i guess i wrote it in wrong way)
Sorry for my long post and my bad english :) Thank you very much
But i want to write data to the same file, so i added data in hardcore way:
Your are making confusion between ms-appx:/// and ms-appdata:/// folders (or ApplicationData.Current.LocalFolder )
The ms-appx folder is read-only. You can't write to it. (or you could edit your app code without passing through the certification process)
The file you wrote must be into the ms-appdata folder.
[edit] I want to clarify that the NullReferenceException does not occur within the posted code, but this code somehow gives back null
I'm getting a NullReferenceException when running my application for the first time, and it happens when I access a list as a property. Here is the code:
/// <summary>
/// Gets the list of workouts using Lazy Loading.
/// </summary>
/// <remarks>
/// This is the point of access for Workouts in this Page.
/// </remarks>
public List<WorkoutModel> Workouts
{
get
{
if (workouts == null || !workouts.Any())
{
workouts = JsonFileHelper.LoadWorkouts();
}
return workouts;
}
}
The JsonFileHelper code that is accessed is here:
/// <summary>
/// Retrieves all the workouts from local storage.
/// </summary>
/// <returns>The list of workouts.</returns>
public static List<WorkoutModel> LoadWorkouts()
{
bool couldLoadFile = true;
List<WorkoutModel> workouts = new List<WorkoutModel>();
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFile textFile = null;
Task<List<WorkoutModel>> t = Task<List<WorkoutModel>>.Run(() => LoadWorkoutsAsync(textFile, localFolder, couldLoadFile));
t.Wait();
workouts = t.Result;
return workouts;
}
Which calls this method on a background thread:
private static async Task<List<WorkoutModel>> LoadWorkoutsAsync(StorageFile textFile, StorageFolder localFolder, bool couldLoadFile)
{
List<WorkoutModel> workouts = new List<WorkoutModel>();
if (localFolder != null)
{
try
{
textFile = await localFolder.GetFileAsync(AppResources.FileName);
}
catch (FileNotFoundException)
{
couldLoadFile = false;
}
if (couldLoadFile)
{
// Create and use a stream to the file atomically
using (IRandomAccessStream textStream = await textFile.OpenReadAsync())
{
// Read the text stream atomically
using (DataReader textReader = new DataReader(textStream))
{
uint length = (uint)textStream.Size;
await textReader.LoadAsync(length);
string data = textReader.ReadString(length);
workouts = JsonConvert.DeserializeObject<List<WorkoutModel>>(data);
}
}
}
}
return workouts;
}
I've noticed that when debugging, the application does not crash - this leads me to believe there is some issue with synchronization going on, because it crashes when the application is run normally. This is my first foray into to asynchronous code, so there's probably something I'm missing.
What could be causing this problem?
Please read the John Saunders question. You need this knowledge, and you should have already found it before posting here.
The code needs to be restructured with variables set predictably on all paths. It's not surprising if you get errors of this kind as it is.
An exception other than FileNotFoundException will leave couldLoadFile as true and textFile as null, triggering this error. This could be your bug.
If this is not enough, then please provide the stack trace.
Instead of using Task.Wait you should try Task.Result.
///
/// Retrieves all the workouts from local storage.
///
/// The list of workouts.
public static List<WorkoutModel> LoadWorkouts()
{
bool couldLoadFile = true;
List<WorkoutModel> workouts = new List<WorkoutModel>();
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFile textFile = null;
List<WorkoutModel> workouts = Task<List<WorkoutModel>>.Run(() => LoadWorkoutsAsync(textFile, localFolder, couldLoadFile)).Result;
return workouts;
}
I want to notify my web view from button in html file and trigger the javascript:
function notify(str) {
window.external.notify(str);
}
The event captured using wv_ScriptNotify(..., ...):
void wv_ScriptNotify(object sender, NotifyEventArgs e)
{
Color c=Colors.Red;
if (e.CallingUri.Scheme =="ms-appx-web" || e.CallingUri.Scheme == "ms-appdata")
{
if (e.Value.ToLower() == "blue") c = Colors.Blue;
else if (e.Value.ToLower() == "green") c = Colors.Green;
}
appendLog(string.Format("Response from script at '{0}': '{1}'", e.CallingUri, e.Value), c);
}
I set the html file on ms-appx-web and it running well, and I realize that the html file must be store into local folder. So I change the ms-appx-web:///.../index.html to ms-appdata:///local/.../index.html.
Already search in microsoft forum and get this. On that thread there is a solution using resolver, but I'm still confusing, how can it notify from javascript like using window.external.notify? And what kind of event in C# side that will capture the "notify" from javascript other than "ScriptNotify"?
Update
There is a solution from here, example using the resolver and it said to use ms-local-stream:// rather than using ms-appdata://local so I can still use the ScriptNotify event. But unfortunately the example using the ms-appx that means using the InstalledLocation not the LocalFolder.
Trying to googling and search in msdn site for the documentation for ms-local-stream but the only documentation is just the format of ms-local-stream without any example like this ms-local-stream://appname_KEY/folder/file.
Based from that documentation, I made some sample to try it:
public sealed class StreamUriWinRTResolver : IUriToStreamResolver
{
/// <summary>
/// The entry point for resolving a Uri to a stream.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
{
if (uri == null)
{
throw new Exception();
}
string path = uri.AbsolutePath;
// Because of the signature of this method, it can't use await, so we
// call into a separate helper method that can use the C# await pattern.
return getContent(path).AsAsyncOperation();
}
/// <summary>
/// Helper that maps the path to package content and resolves the Uri
/// Uses the C# await pattern to coordinate async operations
/// </summary>
private async Task<IInputStream> getContent(string path)
{
// We use a package folder as the source, but the same principle should apply
// when supplying content from other locations
try
{
// My package name is "WebViewResolver"
// The KEY is "MyTag"
string scheme = "ms-local-stream:///WebViewResolver_MyTag/local/MyFolderOnLocal" + path; // Invalid path
// string scheme = "ms-local-stream:///WebViewResolver_MyTag/MyFolderOnLocal" + path; // Invalid path
Uri localUri = new Uri(scheme);
StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri);
IRandomAccessStream stream = await f.OpenAsync(FileAccessMode.Read);
return stream.GetInputStreamAt(0);
}
catch (Exception) { throw new Exception("Invalid path"); }
}
}
And inside my MainPage.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// The 'Host' part of the URI for the ms-local-stream protocol needs to be a combination of the package name
// and an application-defined key, which identifies the specific resolver, in this case 'MyTag'.
Uri url = wv.BuildLocalStreamUri("MyTag", "index.html");
StreamUriWinRTResolver myResolver = new StreamUriWinRTResolver();
// Pass the resolver object to the navigate call.
wv.NavigateToLocalStreamUri(url, myResolver);
}
It always get the exception when it reach the StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri); line.
If anybody ever got this problem and already solved it, please advise.
After debugging it, I found something interesting, the BuildLocalStreamUri part is already make the ms-local-stream automatically.
I made some changes on the getContent method inside StreamUriWinRTResolver class:
public sealed class StreamUriWinRTResolver : IUriToStreamResolver
{
/// <summary>
/// The entry point for resolving a Uri to a stream.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
{
if (uri == null)
{
throw new Exception();
}
string path = uri.AbsolutePath;
// Because of the signature of this method, it can't use await, so we
// call into a separate helper method that can use the C# await pattern.
return getContent(path).AsAsyncOperation();
}
/// <summary>
/// Helper that maps the path to package content and resolves the Uri
/// Uses the C# await pattern to coordinate async operations
/// </summary>
private async Task<IInputStream> getContent(string path)
{
// We use a package folder as the source, but the same principle should apply
// when supplying content from other locations
try
{
// Don't use "ms-appdata:///" on the scheme string, because inside the path
// will contain "/local/MyFolderOnLocal/index.html"
string scheme = "ms-appdata://" + path;
Uri localUri = new Uri(scheme);
StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri);
IRandomAccessStream stream = await f.OpenAsync(FileAccessMode.Read);
return stream.GetInputStreamAt(0);
}
catch (Exception) { throw new Exception("Invalid path"); }
}
}
Change the file path on the MainPage.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// The 'Host' part of the URI for the ms-local-stream protocol needs to be a combination of the package name
// and an application-defined key, which identifies the specific resolver, in this case 'MyTag'.
Uri url = wv.BuildLocalStreamUri("MyTag", "/local/MyFolderOnLocal/index.html");
StreamUriWinRTResolver myResolver = new StreamUriWinRTResolver();
// Pass the resolver object to the navigate call.
wv.NavigateToLocalStreamUri(url, myResolver);
wv.ScriptNotify += wv_ScriptNotify;
}
protected override void wv_ScriptNotify(object sender, NavigationEventArgs e)
{
if (e.CallingUri.Scheme == "ms-local-stream")
{
// Do your work here...
}
}