react-native-webview: Unable to load local file on Android

When trying to open a local webpage from tyhe local file system (using file://) it works on iOS, but on Android we get an error:

Encountered an error loading page:
{
  canGoBack: false,
  canGoForward: false,
  code: -1,
  description: "net::ERR_ACCESS_DENIED",
  loading: false,
  target: 165,
  title: "Webpagina niet beschikbaar",
  url: "file:///data/user/0/com.twipepocv4/cache/data/36/Page-313.html",
}

This is my code:

      <WebView
        originWhitelist={['*']}
        allowFileAccess={true}
        source={{uri: "file:///data/user/0/<myapp>/cache/data/36/Page-319.html"}}
        domStorageEnabled={true}
        allowUniversalAccessFromFileURLs={true}
        allowFileAccessFromFileURLs={true}
      />

I believe this has something to do with AllowFileAccessFromFileURLs but I can’t get it to work. Should any other permission be set?

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 16
  • Comments: 29 (2 by maintainers)

Commits related to this issue

Most upvoted comments

@tjensen is exactly right on the cause. Here’s a more detailed investigation and a workaround that we use. This only applies to Android 10.

Problem

react-native-webview on Android handles the allowFileAccess and allowUniversalAccessFromFileURLs by calling view.getSetings().setAllowFileAccess() in the Java code: https://github.com/react-native-community/react-native-webview/blob/47e9a0b/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java#L484

At the same time, setting source calls view.loadUrl(). This results in a race where if view.loadUrl() executes before the security settings are updated, the page-load will fail.

Workaround

We don’t load your local file until we’re sure the correct permissions have been set on the WebView.

On the component you have that renders <WebView source={...}>:

  1. Create a state variable in the constructor or the class
this.state = { renderedOnce: false };
  1. In componentDidMount, set that variable to be true
componentDidMount() {
  this.setState({ renderedOnce: true });
}
  1. When rendering, render your file only when renderedOnce is true:
render() {
  return <WebView source={renderedOnce ? { uri: indexHtmlPath } : undefined} />;
}

You guys are not doing properly, Proper ways is as follows: First install it using (If not installed), npm install --save react-native-webview // inside project root directory react-native link react-native-webview // it will link WebView with iOS and Android

import React, { Component } from 'react';
import { WebView } from 'react-native-webview';
import {LocalHtml} from './project-root/html/demo.html'; // path of your file starts with "./"
_// as per above example replace it with above LocalHtml_
_//import {LocalHtmlText} from './data/user/0/com.twipepocv4/cache/data/36/Page-313.html';_

class MyInlineWeb extends Component {
  render() {
    return (
      <WebView
        originWhitelist={['*']}
        source={{ html: LocalHtml }} // here it will render your html content and load on webview.
        javaScriptEnabled={true}
        domStorageEnabled={true}
        useWebKit={true}
        startInLoadingState={true}

      />
    );
  }
}

I hope this will help you guys.

So I ran into the same issue. What solved it for me was to add the prop allowFileAccess in the WebView (and also don’t forget to ask the permission READ_EXTERNAL_STORAGE)

It doesn’t require to use prop “allowFileAccess”, you try above code, it will work perfectly.

I use functional components and hooks and got the solution from @hsource to work by updating the setRenderedOnce in a function that I pass to the webview’s onLoad prop:


const [renderedOnce, setRenderedOnce] = useState(false);

const updateSource = () => {
    setRenderedOnce(true);
 };

return(
   <WebView
       originWhitelist={['*']}
       source={renderedOnce ? {uri: localHTML} : undefined}
       style={{flex: 1}}
       allowFileAccess={true}
       allowUniversalAccessFromFileURLs={true}
       onLoad={updateSource}
   />)

This issue should reopen

I tried all solutions suggested above, but ERR_ACCESS_DENIED problem occurs. In my case, I download and save a file to my app’s cache directory (e.g. file:///data/user/0/.../cache/5f8f.../downloaded.pdf) and try to open this file using WebView. My WebView code is:

<WebView
  source={{ uri: file.uri }}
  startInLoadingState
  originWhitelist={['file://']}
  allowUniversalAccessFromFileURLs={true}
  allowFileAccess={true}
/>

On iOS, it works fine. However, on Android, it prints ERR_ACCESS_DENIED. I’ve tried the solution put renderOnce state but it did not work for me.

Any other solution to solve my problem? Thanks, in advance. If you need more code to investigate, please let me know.

@femiveys

Navigate to node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java

Change the following lines:


settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    settings.setAllowFileAccessFromFileURLs(true);
    setAllowUniversalAccessFromFileURLs(webView, true);
}


By default, these props will be false, make it true and you’re good to go. 😃

You guys are not doing properly, Proper ways is as follows: First install it using (If not installed), npm install --save react-native-webview // inside project root directory react-native link react-native-webview // it will link WebView with iOS and Android

import React, { Component } from 'react';
import { WebView } from 'react-native-webview';
import {LocalHtml} from './project-root/html/demo.html'; // path of your file starts with "./"
_// as per above example replace it with above LocalHtml_
_//import {LocalHtmlText} from './data/user/0/com.twipepocv4/cache/data/36/Page-313.html';_

class MyInlineWeb extends Component {
  render() {
    return (
      <WebView
        originWhitelist={['*']}
        source={{ html: LocalHtml }} // here it will render your html content and load on webview.
        javaScriptEnabled={true}
        domStorageEnabled={true}
        useWebKit={true}
        startInLoadingState={true}

      />
    );
  }
}

I hope this will help you

@mayursarode4 What if your path to the html is dynamic? And you don’t know the exact file path at build.

@Shan7anu said:

Navigate to node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java

Change the following lines:


settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    settings.setAllowFileAccessFromFileURLs(true);
    setAllowUniversalAccessFromFileURLs(webView, true);
}

By default, these props will be false, make it true and you’re good to go. 😃

While this is not a fix that I would be comfortable putting into my production code, it does point to the underlying problem, which is that the order in which the WebView props are set may not be deterministic. I added System.out.println calls to the setSource and setAllowFileAccess methods in RNCWebViewManager.java (version 6.11.1), and when the WebView failed to load the local file URL, it was clear that the setSource method was called before setAllowFileAccess was called, so the page was being loaded before the allowFileAccess prop was applied.

It seems like there needs to be a mechanism to store the prop values but not actually call loadUrl/loadDataWithBaseURL until after all props have been stored and their settings have been applied to the WebSettings object for the current WebView instance.

I only use hooks and no classes, so use useEffect and useCallback in this way! This code works for displaying local HTML pages on both IOS and Android. Important! Pay attention to html: use only html:

import React, { useState, useEffect, useCallback } from ‘react’; import { StyleSheet, View } from ‘react-native’; import FusionCharts from ‘react-native-fusioncharts’; import * as FileSystem from ‘expo-file-system’; import { Asset } from ‘expo-asset’;

… here different code does not concern the main idea of the solution

const [libraryPath, setLibraryPath] = useState(null); const getLibraryPath = useCallback(async () => { const indexHtml = await Asset.fromModule( require(‘…/…/…/…/assets/fusioncharts.html’) ).downloadAsync(); return await FileSystem.readAsStringAsync(indexHtml.localUri); });

useEffect(() => { async function setData() { Promise.all([dataFetch, schemaFetch]).then(async (res) => { setState({ …state, dataJson: res[0], schemaJson: res[1] }); setLibraryPath({ html: await getLibraryPath() }); }); } setData(); }, []);

return (<View style={styles.container}> <View style={styles.chartContainer}> <FusionCharts dataJson={ state.dataJson } schemaJson={ state.schemaJson } type={state.type} width={state.width} height={state.height} dataFormat={state.dataFormat} dataSource={state.dataSource} libraryPath={libraryPath} /> </View> </View>);

It works 50/50. Sometimes my locally storaged html renders and sometimes I get ACCESS_DENIED error. Is there any solution? Also, it works fine with release build, but some problems with access on debug build.

You guys are not doing properly, Proper ways is as follows: First install it using (If not installed), npm install --save react-native-webview // inside project root directory react-native link react-native-webview // it will link WebView with iOS and Android

import React, { Component } from 'react';
import { WebView } from 'react-native-webview';
import {LocalHtml} from './project-root/html/demo.html'; // path of your file starts with "./"
_// as per above example replace it with above LocalHtml_
_//import {LocalHtmlText} from './data/user/0/com.twipepocv4/cache/data/36/Page-313.html';_

class MyInlineWeb extends Component {
  render() {
    return (
      <WebView
        originWhitelist={['*']}
        source={{ html: LocalHtml }} // here it will render your html content and load on webview.
        javaScriptEnabled={true}
        domStorageEnabled={true}
        useWebKit={true}
        startInLoadingState={true}

      />
    );
  }
}

I hope this will help you

@mayursarode4 What if your path to the html is dynamic? And you don’t know the exact file path at build.

I tried this. But got Empty white screen. I need to render a html page which import local assests and javascript code in it. Enviroment:

expo: v34.0.1
react-native-webview: v5.12.0
expo-cli: v3.0.4

Here how I am doing it.

import React, { Component } from "react";
import { View } from "react-native";
import { WebView } from "react-native-webview";
import { LocalHtml } from "./d.html";
export default class App extends Component {
  render() {
    return (
      <View style={{ flex: 1 }}>
        <WebView
          style={{ width: "100%", heightL: "100%" }}
          originWhitelist={["*"]}
          source={{ html: LocalHtml }}
          javaScriptEnabled={true}
          domStorageEnabled={true}
          useWebKit={true}
          startInLoadingState={true}
        />
      </View>
    );
  }
}

and html file for testing:

<!DOCTYPE html>
<html>
<body style="background-color: aqua">
  <h1>HTML Images</h1>
  <p>HTML images are defined with the khkjhkj jkhkjhkhkjhjhkhjhkhkhkkjhkjhkjhkhkhhjkhjhjhjkimg tag:</p>
</body>
</html>

So I ran into the same issue. What solved it for me was to add the prop allowFileAccess in the WebView (and also don’t forget to ask the permission READ_EXTERNAL_STORAGE)

Edit: Okay so this worked on the Android emulators, but it didn’t on real devices, so I’m back to using react-native WebView for now. I tried using all 3 props allowFileAccess, allowUniversalAccessFromFileURLs and allowFileAccessFromFileURLs but the error persists…

I also encountered the same problem @imran1992 expo: v34.0.1 react-native-webview: v5.12.0 expo-cli: v3.0.4

@zhouping3 Try above solution by @imran1992 it will help you solve your issue for react-native: 0.60.+ version’s doesn’t require linking for libraries as it have auto linking feature, you just need to install the libraries and build and run the project.

My version details are,

react-native info info Fetching system and libraries information… System: OS: Linux 4.4 Ubuntu 16.04.6 LTS (Xenial Xerus) CPU: (4) x64 Intel® Core™ i5-4440S CPU @ 2.80GHz Memory: 8.86 GB / 15.57 GB Shell: 4.3.48 - /bin/bash Binaries: Node: 12.4.0 - ~/.nvm/versions/node/v12.4.0/bin/node npm: 6.9.0 - ~/.nvm/versions/node/v12.4.0/bin/npm SDKs: Android SDK: API Levels: 21, 22, 23, 25, 26, 27, 28 Build Tools: 21.1.2, 25.0.0, 25.0.2, 25.0.3, 27.0.3, 28.0.2, 28.0.3 System Images: android-25 | Google Play Intel x86 Atom, android-28 | Intel x86 Atom, android-28 |Intel x86 Atom_64 IDEs: Android Studio: 3.4 AI-183.6156.11.34.5692245 npmPackages: react: 16.8.6 => 16.8.6 react-native: 0.60.4 => 0.60.4 npmGlobalPackages: react-native-cli: 2.0.1

I’m not using expo-cli, so don’t know about it

@developeromI89 if you only want to open PDFs you can use ‘react-native-view-pdf

Or you can use append your WebView url like this: "https://docs.google.com/gview?embedded=true&url=$url"

This worked with a local uri (only tested in dev) :

import {useAssets} from 'expo-asset';
import {Platform} from 'react-native';
import {WebView} from 'react-native-webview';
...
  const [assets] = useAssets([require('../../../assets/myhtml.html')]);
  const htmlAsset = assets?.[0];
...
      <WebView
        source={
          htmlAsset
            ? {
                uri:
                  Platform.OS === 'android'
                    ? htmlAsset.localUri
                    : htmlAsset.uri,
              }
            : undefined
        }
        onMessage={event => {
          console.log(JSON.parse(event.nativeEvent.data));
        }}
        startInLoadingState={true}
        originWhitelist={['*']}
        allowFileAccess={true}
        allowUniversalAccessFromFileURLs={true}
      />
...

Using expo-asset and this trick : https://github.com/react-native-webview/react-native-webview/issues/656#issuecomment-551312436

@yaoonline can you please post the entire code? This seems not working to me. Thanks

Hello 👋, this issue has been opened for more than 2 months with no activity on it. If the issue is still here, please keep in mind that we need community support and help to fix it! Just comment something like still searching for solutions and if you found one, please open a pull request! You have 7 days until this gets closed automatically