Accessing file in public folder through capacitor - reactjs

I'm using ionic-v5, capactior and react to build an app.
I have a file (data.json) stored inside of my public/ folder.
I simply want to be able to load that file in and store it as an object.
So far I have tried:
import { Filesystem, FilesystemEncoding } from '#capacitor/core'
let contents = await Filesystem.readFile({
path: "data.json",
encoding: FilesystemEncoding.UTF8,
})
import { HTTP } from '#ionic-native/http';
let response = await HTTP.get("file://data.json", {}, {});
ret = response.data;
return ret;
I have also looked at https://ionicframework.com/docs/native/file but the documentation is poor to say the least.
Along with this I have tried pre-pending /android_asset/public to all of the paths but no luck (I know it would only work on Android, I just wanted to get something).

If you're using Ionic React (v5) and you just want to access a file in /myapp/public, you don't need Capacitor.
You can use axios (or fetch).
Folder structure:
/myapp
/assets
/json
/myFile.json
Sample code:
// https://stackoverflow.com/a/52570060
export const fetchJsonFile = (fileName: string): Promise<any> => (
axios.get(`./json/${fileName}`)
.then((response) => response).catch((error) => {
Promise.reject(error);
})
);

Related

How to load a document giving a path inside the folder with loadDocument() function in PdfTron/ Pdf.js (React App)

I'm having issues using the instance.loadDocument(), to point the path where my pdf are located. If I'm using the initial document path inside WebViewer constructor, everything loads fine.
useEffect(() => {
WebViewer(
{
path: 'lib',
initialDoc: `lib/pdf/mydoc.pdf`,
},
viewer.current
).then((instance) => {
When I try to load an instance instead, I try to point to the same path but I'm getting the error: "Pdf header not found"
.then((instance) => {
const { docViewer, Annotations } = instance;
instance.loadDocument(`lib/pdf/mydoc.pdf`)
const annotManager = docViewer.getAnnotationManager();
How Shall I use this loadDocument function there? Or if I might something else maybe.
Thanks for the help!
Can you open "lib/pdf/mydoc.pdf" in a web browser? Is it accessible from a URL?

Integration testing a fetch calls in a node module

Goal: Call a function that invokes a fetch call to validate it works with my backend rest-api (end to end testing basically).
Project: node module built to be imported into several react web application. The module contains only fetch calls and minimal logic. Its basically a glorified wrapper for URLs and settings. Created to cut down work required to implement common end points used in applications.
Setup: I have a docker compose building a docker test container and pulling in my rest-api docker image (built in a different system). The test container pulls in the packed module and installs it with dependencies. Then it brings up the tests alongside the backend + other images needed for the backend (DB, login system, etc).
Problem: How to implement the tests to handle the calls.
Currently I've tried calling the fetch methods directly. This works for my login fetch but any additional call fails to send the cookie. As far as I understand the code I have depends on the browser for the cookie. I've tried several solutions to get said cookie but i've been unable to get fetch of node-fetch to send it properly. My best guess is each test was creating a new cookie but I lack the knowledge to full debug this solution path.
my send solution path was to attempt to use puppeteer to load a fake page and then evaluate the function in page following examples like:
https://github.com/puppeteer/puppeteer/issues/2579
How to use imported function inside page.evaluate in Puppeteer with Jest?
Problem with this is the tests kept failing to load libraries required or some other problem.
Code:
Here is the call I'm trying to test for the most part. Each function I have wraps around this providing {url: "api/endpoint/path", method: "GET"}. With some passing in a body for larger data posts.
export function request(options) {
//Build options
options = {
headers: {
'Content-Type': 'application/json'
},
...options
};
return fetch(options.url, options)
.then(response => {
//...
//Handle errors
if (!response.ok) {
return Promise.reject(`${response.status} - ${response.statusText}`);
}
try {
return response.json();
} catch (e) {
if (e.name === 'SyntaxError') {
return Promise.reject(response.text());
}
}
});
}
Test example i've tried:
import puppeteer from "puppeteer";
import {myCall} from "my-rest-bridge";
it('Check response', async () => {
//Load browser
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox']
});
const page = await browser.newPage();
//Load page
await page.goto('http://docker:8888/index.html');
//Do login
expect(await page.evaluate(() => login('user', 'password')).toBe(expectedUserResponseJson);
//Make call
expect(await page.evaluate(() => myCall(input1, input2)).toBe(expectedCallResponseJson);
//Close page
await page.close();
})
Took me a while but I built a solution to my own question. Its not perfect so if anyone has a better idea please answer.
So my solution works as follows. I built an addition git project to create a shell reactjs application inside a docker image. This application pulls in my node module, iterates through all the exports, and then generates a component per function.
import React from 'react';
import * as magicNodeModule from "magic-node-module"; //obviously not the real name
import CallRest from "./CallRest";
/** Core component for the application */
export default class App extends React.Component {
/** Renders the core application */
render() {
const restCalls = Object.keys(magicNodeModule);
return (
<div id={"App"}>
<div>
Functions found:
<ul id={"function-list"}>
{restCalls.map(restCall => <li>{restCall}</li>)}
</ul>
<hr/>
</div>
{
restCalls.map(restCall => {
return (
<CallRest restName={restCall} restCall={magicNodeModule[restCall]}/>
);
})
}
</div>
)
}
}
This component (CallRest) contains an input box, submit button, and output div. A user, or in my use case puppeteer, can input data into the input. Then by clicking submit it will run the fetch call and insert the resulting output into the div. Works very much like swagger 2 for those that are familiar with the system.
The solution is still built up as a series of docker images inside of a compose. Though it did require setting up a reverse proxy to allow the react app to communicate with backend API. As well it pulls in a fresh copy of the node module as a pack zip and installs it into the docker. This way I only have to build the shell react docker once in a blue moon.

How to check if asset was added from public/ dir in React?

Is it possible to check if a file exists within the /public/ directory?
I have a set of images that correspond to some objects. When available, I would like to display them using <img> tag. However not all of the objects have a corresponding image, in which case I would like to perform a REST request to our server.
I could create a list of files as part of build process, but I would like to avoid that if possible.
I am using create-react-app if it matters (if I understand correctly fs doesn't work in client-side React apps).
EDIT: I guess I should have been more exact in my question - I know client-side JS can't access this information (except through HTTP requests), I was just hoping something saves information (during build) about the files available in a way that is accessible to client-side Javascript... Maybe Webpack or some extension can do this?
You can do this with your axios by setting relative path to the corresponding images folder. I have done this for getting a json file. You can try the same method for an image file, you may refer these examples
Note: if you have already set an axios instance with baseurl as a server in different domain, you will have to use the full path of the static file server where you deploy the web application.
axios.get('http://localhost:3000/assets/samplepic.png').then((response) => {
console.log(response)
}).catch((error) => {
console.log(error)
})
If the image is found the response will be 200 and if not, it will be 404.
Edit: Also, if the image file is present in assets folder inside src, you can do a require, get the path and do the above call with that path.
var SampleImagePath = require('./assets/samplepic.png');
axios.get(SampleImagePath).then(...)
First of all you should remember about client-server architecture of any web app. If you are using create-react-app you are serving your app via webpack-dev-server. So you should think about how you will host your files for production. Most common ways are:
apache2 / nginx
nodejs
but there is a lot of other ways depending on your stack.
With webpack-dev-server and in case you will use apache2 / nginx and if they would be configured to allow direct file access - it is possible to make direct requests to files. For example your files in public path so
class MyImage extends React.Component {
constructor (props) {
super(props);
this.state = {
isExist: null
}
}
componentDidMount() {
fetch(MY_HOST + '/public/' + this.props.MY_IMAGE_NAME)
.then(
() => {
// request status is 200
this.setState({ isExist: true })
},
() => {
// request is failed
this.setState({ isExist: false })
}
);
}
render() {
if (this.state.isExist === true) {
return <img src={ MY_HOST + "/public/" + this.props.MY_IMAGE_NAME }/>
}
return <img src="/public/no-image.jpg"/>
}
}

create react app Configuration file after build app

I want a Config File (JSON) in root folder after build to config my app.
like Translation and API Urls and ...
Can I do this with create react app?
Create config.js or json file outside src directory and include it in index.html like
<script src="%PUBLIC_URL%/config.js" type="text/javascript"></script>
configure parameters in config.js
config.js
var BASE_URL = "http://YOUR-URL";
you can get paramenters like
const BASE_URL = window.BASE_URL;
You can store you JSON file in the public/ folder and it'll automatically provide this file when you host your Create React App.
Something like: /public/my-configuration-file.json
then when you restart your application:
localhost:3000/my-configuration-file.json
will provide you this json file.
You could create a custom hook that reads a "public" config file using fetch.
// This path is relative to root, e.g. http://localhost/config.json
const configFile = './config.json'
export function useConfig() {
const [config, setConfig] = useState(initialConfig);
useEffect(() => {
(async function fetchConfig() {
try {
const response = await (await fetch(configFile)).json();
setConfig(response);
} catch (e) {
console.log(e);
}
}());
}, []);
return config;
}
Then use it anywhere in you app
function App() {
const config = useConfig();
return (
<div>{config.foo}</div>
);
}
You'll always have an up to date non-cached version of it's data.
updating this topic with a brand new package that is available now that brings the joys of .Net Configuration to the JavaScript world: wj-config.
This package is pretty much an exact answer to what you need. Read this blog post for more information.
It is incredible to me how during over 6 years nobody filled in this gap in React (and JavaScript in general). Anyway, give wj-config a try. I think it will be a positive experience.

Ionic 2 File Plugin usage examples

Does anyone have complete examples about how to use the Cordova Native File Plugin in a Ionic 2/Angular 2 project?
I installed this plugin but the documentation don't seems to make much sense to me due the fact it is fragmented and lacks of a complete example, including all needed imports.
For example, the following example don't shows where objects like LocalFileSystem or window came from.
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
console.log('file system open: ' + fs.name);
fs.root.getFile("newPersistentFile.txt", { create: true, exclusive: false }, function (fileEntry) {
console.log("fileEntry is file?" + fileEntry.isFile.toString());
// fileEntry.name == 'someFile.txt'
// fileEntry.fullPath == '/someFile.txt'
writeFile(fileEntry, null);
}, onErrorCreateFile);
}, onErrorLoadFs);
For example, I need to crate a property file. First I need to check if a file exists on app sandbox storage area, if don't exists I must create it. Then I must open the file write data and save it . How could I do that?
Ionic 2 comes with a Cordova file plugin wrapper:
http://ionicframework.com/docs/v2/native/file/.
The necessary file system paths (e.g. cordova.file.applicationDirectory) you can find here at the documentation of the original plugin:
https://github.com/apache/cordova-plugin-file#where-to-store-files. Note that not all platforms support the same storage paths.
I even managed to build a file browser with it. Use it like so:
import {Component} from '#angular/core';
import {File} from 'ionic-native';
...
File.listDir(cordova.file.applicationDirectory, 'mySubFolder/mySubSubFolder').then(
(files) => {
// do something
}
).catch(
(err) => {
// do something
}
);
Here is an example using IonicNative for an app I am working on where I want
to send an email with a csv file attachment.
import {EmailComposer} from '#ionic-native/email-composer';
import {File} from '#ionic-native/file';
class MyComponent {
constructor(private emailComposer: EmailComposer, private file: File) {
}
testEmail() {
this.file.writeFile(this.file.dataDirectory, 'test.csv', 'hello,world,', {replace: true})
.then(() => {
let email = {
to: 'email#email',
attachments: [
this.file.dataDirectory + 'test.csv'
],
subject: 'subject',
body: 'body text...',
isHtml: true
};
this.emailComposer.open(email);
})
.catch((err) => {
console.error(err);
});
}
}
This was tested with ionic 3.7.0 on IOS.

Resources