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"
    }
  ]
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

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';
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.

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
      })
    }
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

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);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

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;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
分享
微博
QQ
微信
回复
2025-03-25 15:18:35
相关问题
如何使用Image加载沙箱路径图片资源
2539浏览 • 2回复 待解决
HarmonyOS 图片资源加载
569浏览 • 1回复 待解决
HarmonyOS Webview load本地资源的问题
515浏览 • 1回复 待解决
HarmonyOS webview加载本地html问题
1840浏览 • 1回复 待解决
WebView 如何实现长截图
2048浏览 • 1回复 待解决
HarmonyOS WebView后台截图
711浏览 • 1回复 待解决
HarmonyOS WebView 延迟加载图片
757浏览 • 1回复 待解决
Iamge组件如何加载Graphic的资源图片
7535浏览 • 1回复 待解决
HarmonyOS Web本地资源跨域加载异常
549浏览 • 1回复 待解决