ekkis
ekkis

Reputation: 10226

WebView refuses to display images

I've read a tonne of posts about this problem, most of them old so I'm creating a new posting good as of React Native v0.61.5

my problem: I'm retrieving a web page and the files it references, writing them to the app's Documents folder, and attempting to load these into a WebView. These events occur when the user clicks on a button and set the state appropriately

here's how I'm indicating the WebView:

<WebView 
  source={{html: this.state.html, baseUrl: this.state.baseUrl}}
  originWhitelist={['*']}
  allowFileAccessFromFileURLs
  allowFileAccess={true}
/>

and the content of the file. Please note that the logo resides in the same directory as the file that references it:

<h1>per aspera ad astra</h1>
<img src="logo.jpg"/>

the problem I'm having is that I can't see the image. it appears as a small blue image of a question mark. if I look at the baseUrl, it's set to:

file:///Users/ekkis/Library/Developer/CoreSimulator/Devices/9F96B01B-3C66-4ABA-96A5-B58049FB9212/data/Containers/Data/Application/735BD192-FC7B-4708-9973-64C3C9953A0D/Documents

...which seems correct. I'm running this on iOS

I have also tried (as suggested elsewhere):

  source={require(this.state.uri)}

but this fails (I get the error: "Error: TransformError App.js: App.js:Invalid call at line 62:require(this.state.uri)". it makes little sense as require cannot read HTML files but maybe there's supposed to be some black magic there that just isn't working for me. I've also tried simply setting the uri but it also doesn't work (and I can't set the baseUrl in that case)

I'm sure someone out there has done this. it's a base case for this component. I look forward to any guidance

Upvotes: 6

Views: 1905

Answers (1)

Steven
Steven

Reputation: 3813

Below is a working example of using a file within a webview for iOS. It uses the react-native-fs npm package to download a file locally using the imageUrl variable to define what image is downloaded. The URL I used for the example points to a nature photo of a creek.

The following example allows for all images within the folder specified by baseFolderPath as well as within any nested folders.

It may be prudent to keep the same file extension between imageUrl and imageLocalPath variables.

import React, { useState, useEffect } from 'react';
import { Text } from 'react-native';
import { WebView } from 'react-native-webview';
import * as RNFS from 'react-native-fs';

const { DocumentDirectoryPath } = RNFS;
const getFolder = filepath => filepath.split('/').slice(0, -1).join('/');
const ensureFolderExists = folderPath => RNFS.mkdir(folderPath + '/', { NSURLIsExcludedFromBackupKey: true });
const writeFile = async (filepath, content, encoding = 'utf8') => {
  await ensureFolderExists(getFolder(filepath));
  await RNFS.writeFile(filepath, content, encoding);
};
const downloadFile = async (fileUrl, destinationPath) => {
  await ensureFolderExists(getFolder(destinationPath));
  await RNFS.downloadFile({
    fromUrl: fileUrl,
    toFile: destinationPath,
    headers: {},
  }).promise;
  return destinationPath;
};

export default () => {
  const baseLocalPath = `${RNFS.DocumentDirectoryPath}/myFolder`;
  const htmlLocalPath = `${baseLocalPath}/webview.html`;

  const imageLocalPath = `${baseLocalPath}/images/myImage.jpg`;
  const imageUrl = `https://images.pexels.com/photos/3225517/pexels-photo-3225517.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2`;

  const html = `<html><body><img src="${imageLocalPath}" style="width: 100%;height:100%;"></body></html>`;

  const [imageDownloaded, setImageDownloaded] = useState(false);
  const [htmlFileWritten, setHtmlFileWritten] = useState(false);
  useEffect(() => {
    writeFile(htmlLocalPath, html).then(() => setHtmlFileWritten(true));
    downloadFile(imageUrl, imageLocalPath).then(() => setImageDownloaded(true));
  }, []);

  return (
    (htmlFileWritten && imageDownloaded) ? (
      <WebView
        originWhitelist={['*']}
        allowFileAccessFromFileURLs={true}
        allowUniversalAccessFromFileURLs={true}
        allowingReadAccessToURL={baseLocalPath}
        source={{ uri: htmlLocalPath }}
      />
    ) : <Text>Loading</Text>
  );
};

Upvotes: 1

Related Questions