Local storage solutions for large data including images on React Native - react-native

Here's the flow of how my end-product should work:
When the user opens the app for the first time, fetch all the data
i.e., including images(150+) and relevant JSON objects.
On opening the app subsequently, the images and data should load
from local storage i.e., no need for internet at all.
I know it seems weird but this is my use case:
The product is a Wayfinder running on Android Box(55-inch touchscreen TV ) which will be placed in the shopping mall. It will not have access to the internet unless I manually connect it.
Hence it should load the data when opening for the first time i.e. when I'm configuring the application.
Solutions I have come across:
Realm: Local database management with excellent support for react-native - my option right now
Native Async Storage: Not suitable for large data
SQLite: Not comfortable with SQL queries
I'm still looking for options on how differently this problem can be tackled. Also, I'm familiar with Redux.
Thanks.

Check out react-native-fs (or expo-file-system if working with expo).
It is specially designed to store files on the device. In your component, it would look something like this:
const RNFS = require('react-native-fs');
RNFS
.downloadFile({ fromUrl: myURL, toFile: myFilePath })
.promise
.then(res => console.log('Done'));

use pouchDB database , this is work with indexDB local browser database
call XHR request for image and convert response to binary data and store in local database
when need to preview image , get from database and make a blobUrl and show in img tag
axios.get(url, {
progress: false, responseType: 'arraybuffer',
onDownloadProgress: (progressEvent) => {
precent = (100 * progressEvent.loaded / progressEvent.total)
console.log(precent)
}
})
.then(resp => {
//get db
let db = $db.dbModel
//set attach
db.get(doc._id).then((doc) => {
db.putAttachment(doc._id, 'index.mp4', doc._rev, new Blob([new Uint8Array(resp.data)], {type: 'video/mp4'}), 'video/mp4')
.then(res => {
// console.log('success store file')
})
})
})
https://github.com/mohammadnazari110/pwa_offline_video_download

Related

In an Expo React Native app, how to pick a photo, store it locally on the phone, and later upload it to a Node.js service?

I'm building a React Native app with Expo, and I want to include the following workflow: The user takes a picture (either with the camera or picking one from the phone's gallery), which is stored locally on the phone, until the user uploads it some later time to a backend service.
I'm pretty stuck and would appreciate any pointers.
Here is what I have:
I use expo-image-picker to pick a photo from the phone's gallery:
const photo = await launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
base64: true,
quality: 1,
});
Then I store the photo locally as a Base64 string using expo-file-system:
const location = `${FileSystem.documentDirectory}${filename}`;
await FileSystem.writeAsStringAsync(location, photo.base64, {
encoding: FileSystem.EncodingType.Base64
});
I keep information about the storage location, file name, and mime type in an image object. Later, I try to upload that image to my own Node.js backend service with axios, sending the following multi-part form data:
const formdata = new FormData();
formdata.append('file', {
path: image.location,
name: image.filename,
type: image.mimetype
} as any);
The backend service that receives the photo uses multer:
const multer = require('multer');
const upload = multer({ storage: multer.memoryStorage() });
router.post('/photo', upload.single('file'), async (request, response) => {
console.log(request.file);
...
});
What arrives at my service is the following:
{
fieldname: 'file',
originalname: '1653135701413.jpg',
encoding: '7bit',
mimetype: 'image/jpg',
buffer: <Buffer >,
size: 0
}
So no data is transferred. (It seems to be properly stored on the phone, because if I use the Expo filesystem's readStringAsAsync, I do get a pretty long Base64 string.)
What am I missing? Do I need to send the image as a blob? (If I try to do so, then request.file is undefined, so I guess I'm doing something wrong there as well.)
And in general, is there a better way to achieve this workflow in a managed React Native app? (For example, is it ok to store the image as a Base64 string, or would it be better to do this differently?)
Edit:
In the form data, I changed path to uri, and I switched from axios to fetch. Now the backend finally receives the image data. 🥳

electron.js and sql - correct way to set it up?

I am new to electron.js - been reading the documentation and some similar post here:
How do I make a database call from an Electron front end?
Secure Database Connection in ElectronJS Production App?
Electron require() is not defined
How to use preload.js properly in Electron
But it's still not super clear how to properly implement a secure SQL integration. Basically, I want to create a desktop database client. The app will connect to the remote db and users can run all kind of predefined queries and the results will show up in the app.
The documentation says that if you are working with a remote connection you shouldn't run node in the renderer. Should I then require the SQL module in the main process and use IPC to send data back and forth and preload IPCremote?
Thanks for the help
Short answer: yes
Long answer:
Allowing node on your renderer poses a big security risk for your app. It is best practices in this case to create pass a function to your preloader. There are a few options you can use to do this:
Pass a ipcRenderer.invoke function wrapped in another function to your renderer in your preload. You can then invoke a call to your main process which can either send info back via the same function or via sending it via the window.webContents.send command and listening for it on the window api on your renderer. EG:
Preload.js:
const invoke = (channel, args, cb = () => {return}) => {
ipcRenderer.invoke(channel, args).then((res) => {
cb(res);
});
};
const handle = (channel, cb) => {
ipcRenderer.on(channel, function (Event, message) {
cb(Event, message);
});
};
contextBridge.exposeInMainWorld("GlobalApi", {
invoke: invoke,
handle:handle
});
Renderer:
let users
window.GlobalApi.handle("users", (data)=>{users=data})
window.GlobalApi.invoke("get", "users")
or:
let users;
window.GlobalApi.invoke("get", "users", (data)=>{users=data})
Main:
ipcMain.handle("get", async (path) => {
let data = dbFunctions.get(path)
window.webContents.send(
path,
data
);
}
Create a DB interface in your preload script that passes certain invocations to your renderer that when called will return the value that you need from your db. E.G.
Renderer:
let users = window.myCoolApi.get("users");
Preload.js:
let get = function(path){
let data = dbFuncions.readSomeDatafromDB("path");
return data; // Returning the function itself is a no-no shown below
// return dbFuncions.readSomeDatafromDB("path"); Don't do this
}
contextBridge.exposeInMainWorld("myCoolApi", {
get:get
});
There are more options, but these should generally ensure security as far as my knowledge goes.

How to download assets/files(.JSON) and store them inside my app not directly on the user's phone in React Native Expo

am using React Native Expo and I was browsing the web to find a way to download assets, and files to my react native project and came across with this post How to Download image in react native
When the user clicks the download button I want assets/files to be downloaded and stored inside the app not directly on the user's phone. I mean I don't want the users to view the downloaded files or delete them manually.
I just want the downloaded assets/files to be accessible by the React Native app. Am doing this to make the app work offline.
Once the users downloaded the assets/files, the app can use the downloaded assets/files. How can I accomplish that?
Thank you in advance!
If you are using expo managed workflow, then rn-fetch-blob will not work for you.
In that case, Expo File System is probably your way to go.
Firstly, install expo-file-system. See this
Next, for saving files and not letting users delete them manually, store them inside the cache-directory like this
import * as FileSystem from 'expo-file-system';
const downloadAssets = async () => {
let name = "Samplefile.jpg";
const result = FileSystem.createDownloadResumable(
url_Of_the_File_You_Want_to_Download,
FileSystem.cacheDirectory + name
);
const response = await result.downloadAsync();
if (response.status === 200) {
// File successfully saved
} else {
// Some error
}
};
To access this file in your app simple execute this function
import * as FileSystem from 'expo-file-system';
const getFiles = async () => {
const CacheDir = await FileSystem.readDirectoryAsync(
FileSystem.cacheDirectory
);
console.log(CacheDir); //Files are stored here
};

Access Dyson server from React Native ios

I'm using Dyson to host a little mock server for my React Native app, and trying to fetch from the server. The server appears to be running well and when I visit my desired url, http://localhost:3000/stations, in my browser, I get a nice JSON response.
In my React Native action, though, I get Network request failed:
export function fetchStations() {
return dispatch => {
dispatch({ type: "GET_STATIONS_START" });
fetch("http://localhost:3000/stations")
.then(res => {
return res.json();
})
.then(json => {
dispatch({ type: "GET_STATIONS_SUCCESS", payload: json.stations });
})
.catch(error => {
console.warn(error);
dispatch({ type: "GET_STATIONS_FAILURE", payload: error });
});
};
}
Using a static local URL works, and using, say, the Google Maps API works (even though it's not what I want, it's just a sample API to try).
I would think I may be calling the url wrong but it works in the browser so that seems doubtful. My guess is that is has something to do with iOS not liking http requests (only accepting https), unless you set some setting somewhere (I've been through this in native iOS development).
If this is the problem, how do I fix it from React Native? Or, what is the actual problem?
PS. I'm using dyson rather than json-server because for some reason I can't get json-server to work. See my other post. Somewhere here :)
I figured it out. The device (simulator) doesn't have access to localhost so I had to set my url to http://127.0.0.1:3000/stations and it works like a dream.

Download large object from AWS S3

I have an Angular web application in which allows users to download files locally (installers). Some files exceed 1.5 GB in size, which causes the browser to crash (Chrome) when using 'normal' s3.getObject(opts, function(err, data){}) calls, since the entire file binary data is cached....?
I have tried to use other techniques, like streaming (StreamSaver.js), but with no luck.
I am trying to chunk the file data, but in the follow code, the 'httpData' event does not get called until the entire file's binary data is loaded...which seems to defeat the purpose of chunking. I am not understanding this event, or I have something misconfigured.
cache.S3.getObject({ Bucket: 'anduin-installers', Key: filePath })
.on('httpDownloadProgress', function (progress) {
$timeout(function () {
pkg.Download.Progress = Math.floor((progress.loaded / progress.total) * 100.0);
});
})
.on('httpData', function (chunk, response) {
console.log('???');
})
.on('complete', function (response) {
$timeout(function () {
pkg.Download.Active = false;
pkg.Download.Progress = 0;
});
})
.send();
Any ideas no how to make event 'httpData' fire as data chunks are received instead of waiting for th whole file? Or should I go with another solution?
Thanks!