I'm trying to use a prepopulated SQLite DB for my react native app. I'm using Expo and the downloadAsync() Function to load my DB from my assets folder. This works as expected on IOS, as I can load the DB and retrieve the data.
On Android however I just can't get this to work. The db file is there in the internal storage of my emulator, but every time I try to retrieve data, an error occurs since 'there is no such table'.
My guess is that SQLite doesn't properly search for my db but instead creates a new one, where my tables are obviously missing.
I've been trying for over 7 hours now, so I appreciate any kind of help.
Folder structure:
App.js
-assets
--db
---db.db
-src
--connection
---connectionClass
App.js
const App = () => {
const [dbLoaded, setDbLoaded] = useState(false);
if(!dbLoaded){
downloadDB().then((value) => setDbLoaded(value));
return <></>
} else {
return (
<Navigation/>
);
}
};
ConnectionClass.js
export const downloadDB = async () => {
await FileSystem.deleteAsync(`${FileSystem.documentDirectory}SQLite`, {idempotent : true});
await FileSystem.makeDirectoryAsync(`${FileSystem.documentDirectory}SQLite`, {intermediates: true });
return await FileSystem.downloadAsync(
Asset.fromModule(require('../../assets/db/WaKanji.db')).uri,
`${FileSystem.documentDirectory}SQLite/WaKanji.db`
).then(({status}) => {
if(status === 200){
return true
}
return false
}).catch(error => {
console.log('Err\n' + error);
return false;
});
};
Related
I'm trying to save a base64 encoded image in iOS using react-native-share and also Share module from React Native. But both fail when trying the Save Image option.
React Native Share
try {
const sharedResponse = await Share.open({ url: dataUri });
console.log(sharedRes);
} catch (error) {
console.log(error);
}
Share Module
try {
const sharedResponse = await Share.share({ url: dataUri });
console.log(sharedRes);
} catch (error) {
console.log(error);
}
Options other than Save image such as copy, and save to files are working fine.
I have added the following in Info.plist as well
<key>NSPhotoLibraryAddUsageDescription</key>
<string>APP wants to save to photos</string>
This is working fine on the first try in the app's lifetime (When it's asking the permissions from the user). After that this functionality doesn't work.
For some reason you need to write the file to the temp directory first before sharing. I'm not sure the reasoning behind this... but it did fix the issue for me.
const filename = `snapshot.jpeg`; // or some other way to generate filename
const filepath = `${FileSystem.cacheDirectory}/${filename}`;
await FileSystem.writeAsStringAsync(filepath, res.data, { encoding: 'base64' });
const isSharingAvailable = await isAvailableAsync();
if (!isSharingAvailable) {
showAlert('Error', 'Sharing is not available.')
return;
}
if (Platform.OS === 'ios') {
//sharing just the file allows for more applications to be shared too. Adding a message seems to remove many apps from the sharing list
await Share.share({ url: filepath });
}
This strange behaviour had happened because I'm trying to open the Share pop-up above a React Native Modal. The issue didn't occur if I try to hide the Modal before the Share pop-up comes up.
I resolved the issue when storing the image locally before opening the Share Modal.
To store the image i used the npm package 'react-native-fs' and then use it just like this:
import RNFS from "react-native-fs";
function storeFileLocally(url: string): Promise<string> {
const localFile = `${RNFS.DocumentDirectoryPath}/tempFile.jpeg`;
const options: RNFS.DownloadFileOptions = {
fromUrl: url,
toFile: localFile
};
return RNFS.copyFile(url, localFile)
.then(() => 'file://'+localFile)
.catch((error) => {
console.error(error);
return null;
});
}
I have pre-defined sqlite database using DB Browser for SQLite. I have place the db file in the path root/android/app/src/main/assets/www/mysqlite.db, unfortunately I'm unable to
connect. Below are my versioning.
Samsung Galaxy Android 11,
"react-native-sqlite-storage": "^6.0.1",
"react": "17.0.2",
"react-native": "0.65.1",
"#react-navigation/native": "^6.0.4",
My script(I make it simplified):
import SQLite from 'react-native-sqlite-storage';
SQLite.DEBUG(true);
SQLite.enablePromise(false);
export const AppSignIn = (props) => {
const OpenDB = () => {
return new Promise((resolve, reject) => {
global.db = SQLite.openDatabase(
{
name: 'mysqlite.db',
createFromLocation: '~mysqlite.db',
},
() => {
console.log("Connection success!");
},
error => {
console.log(error);
reject();
});
resolve();
});
}
const ReadDB = () => {
return new Promise((resolve) => {
global.db.transaction(function (tx) {
tx.executeSql(
// The rest of the trx
);
resolve();
});
});
}
async function ConnectDB() {
return new Promise(async (resolve, reject) => {
await OpenDB()
.then(async () => {
await ReadDB()
.then(() => {
console.log('YEAY FINALLY');
resolve();
})
})
.catch((error) => {
console.log(error);
reject();
});
});
}
React.useEffect(() => {
(async () => {
await ConnectDB()
.then()
.catch();
})();
}, []);
}
The log writes:
LOG OPEN database: mysqlite.db
LOG SQLite.open({"name":"mysqlite.db","createFromLocation":"~mysqlite.db","dblocation":"nosync","assetFilename":"~mysqlite.db"})
LOG new transaction is waiting for open operation
LOG Phone connected? true, Server connected? true
LOG OPEN database: mysqlite.db failed, aborting any pending transactions
LOG [Error: Could not open database]
I have tried several ways but I'm unable to connect to it.
Move from www to assets folder directly. Uninstall app on phone and run again.
Remove SQLite.enablePromise(false);
react-native link react-native-sqlite-storage
cd android && ./gradlew clean
Follow step to opendatabase call
Try moving your file for android from root/android/app/src/main/assets/www/mysqlite.db -> root/android/app/src/main/assets/mysqlite.db
I finally able to run it on Samsung Galaxy with Android 11. I've tried on Redmi6 with Android 9 and it can run.
I've removing react-native.config.js which contain SQLite
module.exports = {
dependencies: {
"react-native-sqlite-storage": {
platforms: {
android: {
sourceDir: "../node_modules/react-native-sqlite-storage/platforms/android-native",
packageImportPath: "import io.liteglue.SQLitePluginPackage;",
packageInstance: "new SQLitePluginPackage()"
}
}
}
}
};
I also remove the import module import io.liteglue.SQLitePluginPackage; in MainApplication.java and the database finally open.
I'm not sure if this way are absolute. I hope it was temporary as it oppose the way from the tutorial.
I have shopping cart in my android App. I am using Firebase as database. I want to mail cart items as CSV / Excel file as attachment.
First you have to fetch all data from firebase.
Read Data From Firebase database
Then you have to generate csv file from the data.
How to create a .csv on android
After that you can send csv file from its path as an attachment to mail
How to send an email with a file attachment in Android
first install excel4node package in your firebase project, then import this in your index.js
const xl = require('excel4node');
also import these for file handling
const os = require('os');
const path = require('path');
const fs = require('fs');
const tempFilePath = path.join(os.tmpdir(), 'Excel.xlsx');
const storage = admin.storage();
const bucket = storage.bucket();
This is how you return the function should look
exports.writeFireToExcel = functions.https.onCall(async(data, context) => {
// Create a new instance of a Workbook class
const workbook = new xl.Workbook();
// Add Worksheets to the workbook
const worksheet = workbook.addWorksheet('Visa Work List');
const ref = firebaseDb.ref('path');
//firebase functions that return stuff must be done in a transactional way
//start by getting snap
return await ref.once('value').then(snapshot =>{
var style = workbook.createStyle({
font: {
bold : true,
},
});
//write workbook
worksheet.cell(1, 1).string('input').style(style);
//....write the rest of your excel
return
//
}).then(function (){
console.log('workbook filled');
//second part of transation - write the excel file to the temp storage in firebase
//workbook.write doesnt return a promise so ive turned it into a promise function
return new Promise((resolve,reject) =>{
workbook.write(tempFilePath, function (err, stats) {
if (err) {
console.error(err);
reject(err)
}else{
resolve()
}
});
})
}).then(function(){
console.log("File written to: " + tempFilePath);
//read the file and check it exists
return new Promise((resolve,reject) =>{
fs.readFile(tempFilePath, function (err, data) {
if (err) {
reject(err)
}else{
resolve()
}
})
})
}).then(function(){
console.log("writing to bucket");
//write the file to path in firebase storage
var fileName = 'VisaSummaryList.xlsx';
var folderPath = uid + "/excelFile/";
var filePathString = folderPath + fileName;
return bucket.upload(tempFilePath,
{ destination: filePathString}).then(function(){
return filePathString;
})
}).catch(err => {
throw err;
});
});
the function returns a filepath in the firebase storage. In your android app just:
//firebase storage reference, result being whats returned from the firebase function
val fbstore = FirebaseStorage.getInstance().reference.child(result)
fbstore.getFile(myFile)
I'm having an ugly issue that only affect my expo app on Android.
Im trying to upload a base64 image taken with expo ImagePicker to Firebase Storage passing the image value with a http-request made with axios to a Firebase Cloud Function which returns the url of the saved image. This url goes in Firestore, but this is out of reach of my question I think.
My current implementation works flawless in IOS (I can get as many urls as I want, they upload pretty quick actually) but, in Android I only can upload 2 images in a row; when I try for the third time, my app get frozen when reach axios/fetch* statement and gives no clue of whats happened. Console is just as it was before trying the third time and the apps or simulators freeze.
Here you can see this behaviour in a 2 min video:
https://youtu.be/w66iXnKDmdo
When I begun working in this bug I was using fetch instead of axios. At that time the issue was that I was able to upload only one image. It were necessary to close and open the app again to upload one more. Now with axios Im able to upload 2 insted of one, but the problem persist.
This is how I implemented the code:
const imageBase64 = 'QWEpqw0293k01...'
This is how I upload the image to Firebase Cloud Storage:
export const savePhoto = (imageBase64) => {
const db = firebase.firestore();
const docRef = db.collection('Comidas').doc();
return () => {
uploadImageToFirestore(imageBase64)
.then(imageUrl => {
console.log('image-url: ', imageUrl);
docRef.set({ imagen: { uri: imageUrl }, });
})
.catch(err => console.log('error: ', err));
};
};
I made a function helper that allow me to make the http request reusable:
import axios from 'axios';
export const uploadImageToFirestore = (imageBase64) => {
//<--- here is where it get frozen the third time
//<--- console.log() calls three times but not axios
return axios({
method: 'post',
url: 'https://us-central1-menuapp-9feb4.cloudfunctions.net/almacenamientoImagen',
data: {
image: base64
},
})
.then(res => res.data.imageUrl)
.catch(err => console.log('error while uploading base64: ', err));
};
This invoques the following Firebase Cloud Function:
exports = module.exports = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const body = req.body;
console.log('image: ', body.image);
fs.writeFileSync("/tmp/uploaded-image.jpg", body.image, "base64", err => {
console.log(err);
return res.status(500).json({ error: err });
});
const bucket = gcs.bucket("myapp.appspot.com");
const uuid = UUID();
bucket.upload(
"/tmp/uploaded-image.jpg",
{
uploadType: "media",
destination: "/comidas/" + uuid + ".jpg",
metadata: {
metadata: {
contentType: "image/jpeg",
firebaseStorageDownloadTokens: uuid
}
}
},
(err, file) => {
if (!err) {
console.log('url: ', {
imageUrl:
"https://firebasestorage.googleapis.com/v0/b/" +
bucket.name +
"/o/" +
encodeURIComponent(file.name) +
"?alt=media&token=" +
uuid
});
res.status(201).json({
imageUrl:
"https://firebasestorage.googleapis.com/v0/b/" +
bucket.name +
"/o/" +
encodeURIComponent(file.name) +
"?alt=media&token=" +
uuid
});
} else {
console.log(err);
res.status(500).json({ error: err });
}
}
);
});
});
I know that axios it’s not being called because there is no log neither register of the Firebase Cloud Function execution.
I expect this code to upload as many images as user consider he/she needs, not just 2 per app session as it does at this moment
How can I solve this?
So I'm using the most recent version of Ionic2 (v3.4) and I'm trying to get the ionic native SQLite to work. I've been able to create database file and put a table in it like so:
this.sqlite.create({
name: "data.db",
location: "default"
})
.then((db:SQLiteObject) => {
db.executeSql(tableQuery, {})
.then(() => console.log("success"))
.catch(() => console.log("fail"));
})
Inserting works too. But when I try to get the result of a selection:
this.sqlite.create({
name: "data.db",
location: "default"
})
.then((db:SQLiteObject) => {
db.executeSql("SELECT * FROM savedCoupons where itemId=" + itemId, {})
.then((db) => {console.log(JSON.stringify(db))})
.catch(() => console.log("***ERROR WITH SELECT***"));
})
.catch(() => console.log("ERROR: FAILED TO CREATE/OPEN DATABASE."));
I get lost because of the lack of documentation. JSON.stringify() is being run so it would seem the query worked. It returns {"rows":{"length":1}, "rowsAffected":0} and that's it. How do I access the result of the query?
Let you have three columns e.x id,name,lastname in your table.
after querying you can access it like:
db.executeSql('SELECT * FROM student WHERE id='+1, {})
.then(result => {
for (var i = 0; i < result.rows.length; i++) {
console.log("---Id---"+result.rows.item(i).id);
console.log("---Name---"+result.rows.item(i).name);
console.log("---Lastname---"+result.rows.item(i).lastname);
}
});