Android:如何在 webview 中从远程加载的 html 页面引用资源图像

发布于 2024-09-25 16:11:45 字数 1413 浏览 7 评论 0原文

我正在尝试从 WebView 中的 HTML 页面内加载/引用应用程序的资产文件夹中的图像。与大多数示例不同,HTML 页面本身并不位于 asset 文件夹中,而是通过 http 从服务器加载。这个问题的背景是一些性能改进,这些改进应该通过直接从设备加载静态图像来减少加载时间(以及传输的数据量)。我不确定 Android 在这里是否有一些限制,因为有一定的可能性通过允许从远程加载的网页访问本地文件存储来利用该应用程序。

我首先尝试使用 加载图像,但失败了(出于明显的原因)。我的下一次尝试是使用 ContentProvider 类并使用 引用图像。这个 ContentProvider 的实现如下:

public class AssetContentProvider extends ContentProvider
{
private static final String URI_PREFIX = "content://com.myapp.assetcontentprovider";

public static String constructUri(String url) {
    Uri uri = Uri.parse(url);
    return uri.isAbsolute() ? url : URI_PREFIX + url;
}

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    Log.d("AssetContentProvider", uri.getPath());
    try {
        return getContext().getAssets().openFd(uri.getPath().substring(1)).getParcelFileDescriptor();
    } catch (IOException e) {
        Log.d("AssetContentProvider", "IOException for " + uri.getPath());
        throw new FileNotFoundException();
    }
}

// more methods irrelevant for this post
}

加载 HTML 页面时,我可以在调试日志中看到 openFile() 实际上是从 WebView 触发的,并且它返回一个有效的 ParcelFileDescriptor 对象但图像仍然不显示。日志中没有显示任何错误消息来告诉我 WebView 拒绝加载/显示图像。有什么想法可以实现吗?

I'm trying to load/reference images from the app's assets folder from within a HTML page in a WebView. Unlike in most of the examples the HTML page itself is not located in the assets folder but is loaded from a server via http. The background of this question are some performance improvements which should reduce the loading time (and the amount of transferred data) by loading static images directly from the device. I'm not sure if Android has some restrictions here because there's a certain possibility to exploit the app by allowing access to the local file storage from a remotely loaded webpage.

I first tried to load images using <img src="file:///android_asset/myimage.png"> but this failed (for obvious reasons). My next attempt was to use a ContentProvider class and reference images using <img src="content://com.myapp.assetcontentprovider/myimage.png">. This ContentProvider is implemented as follows:

public class AssetContentProvider extends ContentProvider
{
private static final String URI_PREFIX = "content://com.myapp.assetcontentprovider";

public static String constructUri(String url) {
    Uri uri = Uri.parse(url);
    return uri.isAbsolute() ? url : URI_PREFIX + url;
}

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    Log.d("AssetContentProvider", uri.getPath());
    try {
        return getContext().getAssets().openFd(uri.getPath().substring(1)).getParcelFileDescriptor();
    } catch (IOException e) {
        Log.d("AssetContentProvider", "IOException for " + uri.getPath());
        throw new FileNotFoundException();
    }
}

// more methods irrelevant for this post
}

When loading the HTML page I can see in the debug log that openFile() is actually triggered from the WebView and it returns a valid ParcelFileDescriptor object but still the image is not displayed. There are no error messages shown in the log which would tell me that WebView refused to load/display the image. Any ideas if and how this could work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

生来就爱笑 2024-10-02 16:11:45

好的,多亏了 mufumbo 的回答,我现在找到了一个有效的技巧,可以在远程加载的 HTML 页面中混合本地资源。使用 WebView 的 loadUrl() 方法加载的页面不会加载与 file:///android_asset/ 链接的图像...作为解决方法,您可以使用 org.apache.http 获取 HTML 页面.client.methods.HttpGet.HttpGet(),然后使用 loadDataWithBaseURL() 将其传递给 WebView。在这种情况下,WebView 将加载与 file:///android_asset/ 链接的资源以及通过 HTTP 的图像和脚本。这是我定制的 webview 代码:

public class CustomWebView extends WebView {
    private String mURL;

    public void loadUrlWithAssets(final String url) {
        // copy url to member to allow inner classes accessing it
        mURL = url;

        new Thread(new Runnable() {
            public void run() {
                String html;
                try {
                    html = NetUtil.httpGETResponse(mURL);

                    // replace some file paths in html with file:///android_asset/...

                    loadDataWithBaseURL(mURL, html, "text/html", "UTF-8", "");
                }
                catch (IOException e) {
                    Log.e("CustomWebView.loadUrlWithAssets", "IOException", e);
                }
            }
        }).start();
    }
}

请注意,整个 http 获取都包含在自制的实用程序类 NetUtil 中。

使用此类,可以从网络服务器渲染 HTML 页面,并从应用程序的资源文件夹加载一些静态资源,例如图像或样式表,以提高加载速度并节省带宽。

OK, thanks to mufumbo's answer I now found a working hack to mix local assets in remotely loaded HTML pages. Pages loaded using the WebView's loadUrl() method do not load images linked with file:///android_asset/... As a workaround you can fetch the HTML page using org.apache.http.client.methods.HttpGet.HttpGet() and then pass it to the WebView with loadDataWithBaseURL(). In this case the WebView will load resources linked with file:///android_asset/ as well as images and scripts via HTTP. Here's my customized webview code:

public class CustomWebView extends WebView {
    private String mURL;

    public void loadUrlWithAssets(final String url) {
        // copy url to member to allow inner classes accessing it
        mURL = url;

        new Thread(new Runnable() {
            public void run() {
                String html;
                try {
                    html = NetUtil.httpGETResponse(mURL);

                    // replace some file paths in html with file:///android_asset/...

                    loadDataWithBaseURL(mURL, html, "text/html", "UTF-8", "");
                }
                catch (IOException e) {
                    Log.e("CustomWebView.loadUrlWithAssets", "IOException", e);
                }
            }
        }).start();
    }
}

Please note that the whole http fetching is wrapped in the home-grown utility class NetUtil.

With this class it's possible to render HTML pages from a webserver and have some static resources like images or stylesheets loaded from the app's asset folder in order to improve loading speed and to save bandwidth.

山人契 2024-10-02 16:11:45

这就是我在java部分的做法:

String myHTML = "< img src=\"file:///android_asset/myimage.jpg\"";
myWebView.loadDataWithBaseURL("file:///android_asset/", myHTML, "text/html", "UTF-8", "");

干杯

This is how I do on the java part:

String myHTML = "< img src=\"file:///android_asset/myimage.jpg\"";
myWebView.loadDataWithBaseURL("file:///android_asset/", myHTML, "text/html", "UTF-8", "");

cheers

北城半夏 2024-10-02 16:11:45

因此,我只能替换页面的图像,如下所示,保持其他实现完好无损。

fun shouldIntercept(
    request: WebResourceRequest,
    webView: WebView,
    documentUrl: String?,
    webViewClientListener: WebViewClientListener?,
): WebResourceResponse? {
    val url = request.url

    listOf(".png", ".jpg", "jpeg", ".gif").forEach {
        if (url.toString().contains(it, ignoreCase = true)) {
            Timber.tag("shouldIntercept-image").e(url.toString())

            val `is`: InputStream = webView.context.assets.open("img/icon_128.png")

            return WebResourceResponse("image/png", "UTF-8", `is`)
        }
    }

So, I was able to replace only the Images of a Page like below keeping other implementation intact.

fun shouldIntercept(
    request: WebResourceRequest,
    webView: WebView,
    documentUrl: String?,
    webViewClientListener: WebViewClientListener?,
): WebResourceResponse? {
    val url = request.url

    listOf(".png", ".jpg", "jpeg", ".gif").forEach {
        if (url.toString().contains(it, ignoreCase = true)) {
            Timber.tag("shouldIntercept-image").e(url.toString())

            val `is`: InputStream = webView.context.assets.open("img/icon_128.png")

            return WebResourceResponse("image/png", "UTF-8", `is`)
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文