webview如何拦截图片加载,使用本地缓存资源?

需求:某些固定的网页需要加载的图片较大,首次加载速度较慢,导致用户体验较差。

现在计划在 app 中内置一些图片资源,在 webview 加载对应图片时,替换为本地图片展示。

相当于对网页图片提前做了缓存,应该如何实现?

HarmonyOS
webview
2025-03-25 13:37:05
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
知识浅谈

以下是个人一些建议,望采纳!

要实现网页图片的本地缓存替换,可以通过拦截 WebView 的资源请求并将特定图片替换为本地资源。以下是完整的实现方案:

1. 实现原理

  1. 建立URL与本地资源的映射关系
  2. 拦截WebView的资源请求
  3. 对匹配的图片请求返回本地资源
  4. 不匹配的请求继续走网络加载

2. 具体实现步骤

2.1 创建图片映射配置文件

在​​resources/base/profile/​​下创建​​image_mapping.json​​:

{
  "mappings": [
    {
      "url": "https://example.com/images/logo.png",
      "local": "entry/resources/base/media/web_cache/logo.png"
    },
    {
      "url": "https://example.com/images/banner.jpg",
      "local": "entry/resources/base/media/web_cache/banner.jpg"
    }
  ]
}

2.2 实现资源拦截器

import web_webview from '@ohos.web.webview';
import fileio from '@ohos.fileio';

class WebResourceInterceptor {
  private imageMappings: Map<string, string> = new Map();

  constructor() {
    this.loadImageMappings();
  }

  private loadImageMappings() {
    try {
      const context = getContext(this) as common.UIAbilityContext;
      const profile = context.resourceManager.getRawFileContentSync('image_mapping.json');
      const jsonStr = String.fromCharCode.apply(null, new Uint8Array(profile));
      const config = JSON.parse(jsonStr);
      
      config.mappings.forEach(item => {
        this.imageMappings.set(item.url, item.local);
      });
    } catch (e) {
      console.error('Load image mappings failed:', e);
    }
  }

  setupInterceptor(webview: web_webview.WebviewController) {
    webview.onInterceptRequest((event) => {
      const url = event.request.getRequestUrl();
      
      // 检查是否是图片请求且在我们的映射表中
      if (this.isImageRequest(url) && this.imageMappings.has(url)) {
        const localPath = this.imageMappings.get(url);
        try {
          const context = getContext(this) as common.UIAbilityContext;
          const fd = context.resourceManager.getRawFd(localPath);
          const fileStream = fileio.createStreamSync(fd);
          
          // 构建响应
          const response = new web_webview.WebResourceResponse();
          response.setResponseData(fileStream);
          response.setResponseMimeType(this.getMimeType(url));
          response.setResponseEncoding('identity');
          response.setResponseCode(200);
          response.setReasonPhrase('OK');
          
          return response;
        } catch (e) {
          console.error('Load local image failed:', e);
          return null; // 回退到网络加载
        }
      }
      return null; // 其他请求不拦截
    });
  }

  private isImageRequest(url: string): boolean {
    return /\.(jpg|jpeg|png|gif|webp)(\?.*)?$/i.test(url);
  }

  private getMimeType(url: string): string {
    if (url.endsWith('.png')) return 'image/png';
    if (url.endsWith('.jpg') || url.endsWith('.jpeg')) return 'image/jpeg';
    if (url.endsWith('.gif')) return 'image/gif';
    if (url.endsWith('.webp')) return 'image/webp';
    return 'application/octet-stream';
  }
}

2.3 在页面中使用

import web_webview from '@ohos.web.webview';

@Entry
@Component
struct WebPage {
  controller: web_webview.WebviewController = new web_webview.WebviewController();
  interceptor: WebResourceInterceptor = new WebResourceInterceptor();

  aboutToAppear() {
    this.interceptor.setupInterceptor(this.controller);
  }

  build() {
    Column() {
      Web({
        src: 'https://example.com',
        controller: this.controller
      })
    }
  }
}

3. 高级优化方案

3.1 动态更新映射表

可以从服务器获取最新的映射关系,实现热更新:

async updateMappingsFromServer() {
  try {
    const http = new http.Http();
    const response = await http.request(
      'https://your-server.com/api/image_mappings',
      { method: 'GET' }
    );
    
    const newMappings = JSON.parse(response.result);
    this.imageMappings.clear();
    
    newMappings.mappings.forEach(item => {
      this.imageMappings.set(item.url, item.local);
    });
    
    // 可以存储到本地,下次启动时直接读取
    const preferences = await data_preferences.getPreferences(getContext(this), 'web_cache');
    await preferences.put('image_mappings', JSON.stringify(newMappings));
  } catch (e) {
    console.error('Update mappings failed:', e);
  }
}

3.2 本地资源预加载

在应用启动时预加载本地图片到内存:

private async preloadImages() {
  const context = getContext(this);
  const imageCache = new Map<string, ArrayBuffer>();
  
  for (const [url, localPath] of this.imageMappings) {
    try {
      const fd = context.resourceManager.getRawFd(localPath);
      const fileContent = fileio.readSync(fd);
      imageCache.set(url, fileContent.buffer);
    } catch (e) {
      console.error(`Preload ${localPath} failed:`, e);
    }
  }
  
  return imageCache;
}
分享
微博
QQ
微信
回复
2025-03-25 15:18:35
相关问题
如何使用Image加载沙箱路径图片资源
3143浏览 • 2回复 待解决
HarmonyOS Webview load本地资源的问题
670浏览 • 1回复 待解决
HarmonyOS 图片资源加载
725浏览 • 1回复 待解决
HarmonyOS webview加载本地html问题
2178浏览 • 1回复 待解决
WebView 如何实现长截图
2380浏览 • 1回复 待解决
HarmonyOS Web本地资源跨域加载异常
727浏览 • 1回复 待解决
Iamge组件如何加载Graphic的资源图片
7708浏览 • 1回复 待解决
HarmonyOS WebView 延迟加载图片
900浏览 • 1回复 待解决
HarmonyOS WebView后台截图
888浏览 • 1回复 待解决
HarmonyOS WebView截图API
934浏览 • 1回复 待解决