I am trying to figure out the best way to store data in my cross platform app I am developing using PhoneGap.The API instructions suggest using WebSQL however this will no longer be supported, and IndexedDB is currently only for Windows / Blackberry.
There are many different questions on here many of the answers are really old and this, but I cannot seem to find the most popular js library for shipping an app with an existing database? (I.e. a good helper .js to simplify things).
I looked at HTML5SQL but the documentation is very sparse, lawnchair I am not sure about.
I don't know if this question will get flagged for being more preference bound, but I will give my 2 cents if it can help.
I have been able to ship an app with an existing database using SQLite with Phonegap/Cordova. Basically how I did it in Android was using the lite4cordova SQLite plugin:
https://github.com/lite4cordova/Cordova-SQLitePlugin
In the native code on load, I would check the default directory to see if a database with a specific name exists:
try {
File dbFile = getDatabasePath("data.db");
Log.v("info", "dbfiledir: " + dbFile.getAbsolutePath());
if (!dbFile.exists()) {
this.copy("data.db", dbFile.getAbsolutePath());
}
} catch (Exception e) {
e.printStackTrace();
}
And if it does not exist, copy the database file from the assets folder to the default database directory:
void copy(String file, String folder) throws IOException {
File CheckDirectory;
CheckDirectory = new File(folder);
String parentPath = CheckDirectory.getParent();
File filedir = new File(parentPath);
//File file2 = new File(file);
//Log.v("info", "filedir: " + file2.getAbsolutePath());
if (!filedir.exists()) {
if (!filedir.mkdirs()) {
return;
}
}
InputStream in = this.getApplicationContext().getAssets().open(file);
File newfile = new File(folder);
OutputStream out = new FileOutputStream(newfile);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0)
out.write(buf, 0, len);
in.close();
out.close();
}
Now when the app loads, it will only copy if the database does not already exist. I can open the database using simply (Syntax may vary depending on plugin):
db = window.sqlitePlugin.openDatabase({
name : "data"
});
I will soon be performing the same logic on the iOS side, but I think it should be similarly straightforward.
That being said, there is a plugin listed on http://plugreg.com that provides a helper library for shipping prepopulated databases with WebSQL:
https://github.com/Smile-SA/cordova-plugin-websqldatabase-initializer
I wanted to use SQLite for my project so I opted for the copy on load method. Good luck!
Latest Update:
New Cross Platform Cordova WebSQL plugin by MS Open Tech
Microsoft Open Technologies is publishing the new open source WebSQL plugin for Apache Cordova and PhoneGap. This plugin allows developers to integrate a persistent SQL-based local storage solution in their Cordova apps using the exact same JavaScript code across Android, iOS, Windows Phone and Windows Store.
Related
I'm using this section of this official MSDN tutorial: Use a SQLite database in a UWP app but I'm getting the following error:
REMARK: There are many online posts related (or similar) to this issue but none seems to have a solution. Most of these posts are a few years old so I thought this issue would have been resolved by now. Moreover, the above mentioned tutorial is using .NET Standard Class library project, as well. And the online posts regarding the issue do not have .NET Standard involved. So, I was wondering if the issue is caused by the use of .NET Standard library. Regardless, a solution will be greatly appreciated.
SQLite Error 14: 'unable to open database file'
Error occurs at line db.Open() of this code:
public static void InitializeDatabase()
{
using (SqliteConnection db =
new SqliteConnection("Filename=sqliteSample.db"))
{
db.Open();
String tableCommand = "CREATE TABLE IF NOT " +
"EXISTS MyTable (Primary_Key INTEGER PRIMARY KEY, " +
"Text_Entry NVARCHAR(2048) NULL)";
SqliteCommand createTable = new SqliteCommand(tableCommand, db);
createTable.ExecuteReader();
}
}
NOTES:
The line just below the above code reads: This code creates the SQLite database and stores it in the application's local data store. That means the app should have access to that local data store.
I'm using latest version 16.3.5 of VS2019 on Windows 10. The target version on the project is selected as Windows 10 1903 and min version as Windows 10 1903
UPDATE
This similar official 3 years old sample works fine. So, the problem seems to be related to newer versions of .NET Core. But I need to use latest version of .NET Core for other features my app is using that are not available in the older versions.
I also tried this similar old tutorial, but it did not on new version of .NET Core work either - giving exact same error.
The old problem reported in 2016 here to Microsoft seems to have resurfaced again with the new version of .NET Core.
This is a misunderstanding, SqliteConnection db = new SqliteConnection("Filename=sqliteSample.db") can not create a Sqlite file, but access the existing Sqlite database file through the path.
So you need to create a valid sqliteSample.db file and place it in the root directory of the UWP project. Select the content in the Properties -> Build operation to ensure it will be loaded into the application directory.
Update
Please create the sqliteSample.db file in LocalFolder first.
await ApplicationData.Current.LocalFolder.CreateFileAsync("sqliteSample.db", CreationCollisionOption.OpenIfExists);
Then use the path to access the database file
string path = Path.Combine(ApplicationData.Current.LocalFolder.Path, "sqliteSample.db");
using (SqliteConnection db =
new SqliteConnection($"Filename={path}"))
{
// ...
}
Best regards.
I created a SQL Lite database and embbed in my app as a android asset the question is where the heck is the file stored on the device I used the following code to access the database
public Database()
{
var path = Path.Combine(System.Environment.
GetFolderPath(System.Environment.
SpecialFolder.Personal), "StockApp.db");
Console.Write("OPening Database dbPath" + path);
database = new SQLiteAsyncConnection(path);
}
When I search for the file i cannot find it but when I use the internal debugger it puts the file at the following location.
It finds it fine and it does place a row in the table
path "/data/user/0/com.companyname.StockApp/files/StockApp.db" string
But where is the actual file I have a STK7 DEVICE for testing so Do any ideas?
I have been trying to install a tiny simple database into my titanium project with the code from the documentation and trying different tweaks as suggested by a variety of sources. But I cannot get it to work, I get errors like "no such table" or resultset is null. Currenty my code looks like this:
var db = Ti.Database.install('../assets/exercises.db', 'exercisesDB');
Ti.API.info('installed '+ db.getName() );
db.close();
Ti.API.info('closed db' );
db = Ti.Database.open('exercisesDB');
Ti.API.info('reopened db' );
//Ti.API.info(db.getName() );
var exercisesDBRS = db.execute('SELECT id,name FROM exercise');
I have tried putting the database file exercises.db in the assets folder and in the Resources folder but I'm getting no where. I created the db file with "DB Browser for SQLite" ver 3.9.1 on OSX Sierra - is it compatible with appcelerator alloy projects? My current code produces the error "no such table" on the execute call. I assure you the db file has an exercise table.
PS, full code in new projects index.js file is as follow:
var db = Ti.Database.install('exercises.sqlite', 'exercisesDB');
Ti.API.debug('installed '+ db.getName() );
db.close();
Ti.API.debug('closed db' );
db = Ti.Database.open('exercisesDB');
Ti.API.debug('reopened db' );
var exercisesDBRS = db.execute('SELECT id, name FROM exercise');
Ti.API.debug('executed select');
Ti.API.debug(' rowcount== '+ exercisesDBRS.rowCount);
while (exercisesDBRS.isValidRow()) {
var exId = exercisesDBRS.fieldByName('id');
var exName = exercisesDBRS.fieldByName('name');
Ti.API.debug("Exercise: "+exId + ' ' + exName );
exercisesDBRS.next();
}
exercisesDBRS.close();
db.close();
function doClick(e) {
alert($.label.text);
}
$.index.open();
New project but same code and a copy of the same database file in the app/lib folder. Same error - no such table on the execute line when tested in android, works fine in iOS sim.
As per the docs, your external database should be located at the same place where you are running the below code:
var db = Ti.Database.install('../assets/exercises.db', 'exercisesDB');
There's no need to use ../assets/. You can simply refer the database without it because everything you put in assets folder is moved to respective Resources->iphone/android folder.
You can always use Ti.Database.install method for external database because after installing a database once, it will behave like Ti.Database.open('exercisesDB') method.
Coming to your primary concern, you can always safely put your database file in app -> lib folder (create lib folder if it's not present and put db file in there then).
After putting the db file there, you can always use below code:
var db = Ti.Database.install('exercises.db', 'exercisesDB');
Though, I have never used .db extension, instead I have always used .sqlite format (since docs also says SQLite database) and it has worked 100% correct every time.
So you can try this process by changing the database format to .sqlite and put it in app->lib folder. It will definitely work and after verifying you can use same code on using a .db file and see if it's supported or not.
On Android: Follow these steps:
1- In your PC, go to the project's root directory, delete build and Resources folders.
2- On device, clear app data from Settings and then delete the app.
3- Now install the app again with the same code and flow as I mentioned here.
If it's working fine for iOS sim, then it should also work for Android (only some cleaning issues are there or your previous database is still there).
I am modifying the N-10-KittensDb sample solution.
I See how to create a SQLite database, but I wish to use an existing database. I am guessing that I need to copy the database to the proper UI data folder. Maybe it is done within the Core project? And if so how is the correct path injected into the running Exec? As the Core can be used across many UI's? What method is called to see if the database exists or needs to be copied?
Sample from DataService:
public DataService(ISQLiteConnectionFactory factory)
{
const string file = "Cats.sldb";
var connection = factory.Create(file);
connection.CreateTable<Kitten>();
}
I believe the paths are different for Android vs Phone vs Touch vs Wpf?
Please direct me to a sample piece of code that uses the Cirrious.MvvmCross.Plugins.Sqlite for Phone or Wpf.
Thank you
Dan
Each platform by default creates a database in a folder location appropriate for the platform - e.g. Touch uses:
public ISQLiteConnection Create(string address)
{
var path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
return new SQLiteConnection(Path.Combine(path, address));
}
from https://github.com/slodge/MvvmCross/blob/v3/Plugins/Cirrious/Sqlite/Cirrious.MvvmCross.Plugins.Sqlite.Touch/MvxTouchSQLiteConnectionFactory.cs#L18
To read/write files, MvvmCross does bundle a File plugin - this also operates by default in platform specific locations - but the two may not match perfectly - e.g. see:
protected override string FullPath(string path)
{
if (path.StartsWith(ResScheme))
return path.Substring(ResScheme.Length);
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), path);
}
from https://github.com/slodge/MvvmCross/blob/v3/Plugins/Cirrious/File/Cirrious.MvvmCross.Plugins.File.Touch/MvxTouchFileStore.cs#L22
Because of this mismatch, in order to share the same database-specific copy code across platforms you may find it easier to just inject your own platform specific copy on each platform - for more on injecting platform specific services, see http://slodge.blogspot.co.uk/2013/06/n31-injection-platform-specific.html
We're replacing an "old" web application (the original CodeCentral was written over 10 years ago, now running at http://cc.embarcadero.com with over 12.7 million served) that uses a file based cache for serving downloads. The web application manages this file cache.
The only complicated bit of using file-based downloads is updating a file that might be in use by another version. Back when I wrote the the original Delphi CGI version of CodeCentral, I vaguely recall being able to rename the existing file even though someone might be downloading it, writing the new file with the correct name, and eventually cleaning up the old file.
However, I may be delusional, because that's not working with our current IIS 6 and ASP.NET 2.x or 3.x code. So, what we've done instead is this, which is quite kludgy but eventually works:
public static void FileCreate(Stream AStream, string AFileName, int AWaitInterval)
{
DateTime until = DateTime.Now.AddSeconds(AWaitInterval);
bool written = false;
while (!written && (until > DateTime.Now))
{
try
{
using (FileStream fs = new FileStream(AFileName, FileMode.Create))
{
CopyStream(AStream, fs);
written = true;
}
}
catch (IOException)
{
Thread.Sleep(500);
}
}
}
I'm thinking we're missing something simple and there's got to be a better way to replace a file currently being downloaded via IIS and ASP.NET. Is there?
I think we're going to resolve this with the following logic:
We store the date/time a file is updated. If we're unable to update the data-driven file name "[id].zip", we'll create "[id]_[modified].zip" instead, and use that until we're able to rename it to "[id].zip" by deleting the older version of the file.
That way, we're always assured of the latest file by first looking for "[id]_[modified].zip" before looking for "[id].zip", and we can easily implement a clean-up routine both on demand and in the background even if there are multiple modified versions before we can finally replace the "standard" "[id].zip" version.
Of course, this only works if you store the last time the file was modified, but any system using this type of technique should anyway.
John, Could you shed a bit more light on the situatuation?
If the files are small enough you could load the entire file into memory then send it to the client.
Maybe this blog post about downloading and deleting temporary files will help a little.