鸿蒙:启动本地 http-server 加载 h5 游戏

发布于:2025-06-21 ⋅ 阅读:(12) ⋅ 点赞:(0)

在这里插入图片描述

之前也讲过
Android 端启动本地 http-server 加载 h5游戏:Android:启动本地 http-server 加载 h5 游戏

原理相似,都是使用 webview 加载本地资源游戏入口,实现通过本机 localhost 加载其他游戏资源

区别:

  • 鸿蒙侧有哪些 http-server 框架可用?
  • 鸿蒙侧资源拦截实现(实现读取资源的真实路径)

1、安装依赖

仓库找的 http-server 依赖,用于启动本地 http 服务,可以让我们访问 http://127.0.0.1

ohpm i @webabcd/harmony-httpserver

在这里插入图片描述

2、自定义 page 使用 Web 组件

主要介绍点:

【1】路由跳转到 har 包内页面:

  • 可使用 router.replaceNamedRoute
  • page 定义路由名称 @Entry({ routeName: GlobalUrl.ROUTER_NAME_H5_GAME_PAGE })
  • 注意导入页面,否则报错找不到路径 import('../../pages/H5GamePage');

【2】应用侧与 H5 侧的交互通道

  • 官方文档:前端页面调用应用侧函数
  • Android 端交互需要设置 webView.addJavascriptInterface(new InJavaScriptLocalObj(), "game_js_object");
  • 鸿蒙端交互在 Web 组件调用 javaScriptProxy

【3】找到游戏入口 index.html 文件

  • 游戏资源文件可以存放在该目录下 src/main/resources/resfile
  • 通过getContext(this).resourceDir 获得 resfile目录,使用 recursiveDirFindFileSync 递归查找到 index.html 文件链接
  • 获取 index.html 文件父目录路径,为后续资源请求路径重定向读取资源真实路径做准备

【4】Web 请求资源重定向

  • 重写 Web 组件 onInterceptRequest 请求拦截,重定向返回请求资源
    【5】启动 http-server 服务
  • 使用第三方依赖 httpserver
import { webview } from '@kit.ArkWeb';
import fs from '@ohos.file.fs';
import { httpServer } from '@webabcd/harmony-httpserver';
import { BusinessError } from '@kit.BasicServicesKit';
import { JSON } from '@kit.ArkTS';


//【1】
@Entry({ routeName: GlobalUrl.ROUTER_NAME_H5_GAME_PAGE })
@Component
export struct H5GamePage {
  private controller: webview.WebviewController = new webview.WebviewController();
  /**
   * 游戏根目录:index.html 父目录路径
   */
  private readonly DEFAULT_PORT: number = 8082;
  /**
   * js 交互【2】
   */
  private readonly GAME_JS_OBJECT: string = "game_js_object";
  private h5JavaScriptObj: H5JavaScriptObj = new H5JavaScriptObj();
  /**
   * http 服务
   */
  private serverPort: number = this.DEFAULT_PORT;
  private host: string = "http://127.0.0.1:";
  /**
   * 本地地址 + index.html 路径
   */
  @State httpGameUrl: string = "";
  private gameRootPath: string = "";

  async aboutToAppear(): Promise<void> {
    this.initParams();
  }

  private async initParams() {
    await KVUtils.getNumber(Constant.KEY_HTTP_PORT).then((value) => {
      if (value !== undefined) {
        this.serverPort = value;
        this.startHttpServer();
      }
    });

	//【3】
    let path = getContext(this).resourceDir;
    const indexHtmlPath = FileUtil.recursiveDirFindFileSync(path, "index.html");
    if (!CommonUtil.isEmpty(indexHtmlPath)) {
      this.gameRootPath = fs.openSync(indexHtmlPath).getParent();
    }
    this.httpGameUrl = this.host + this.serverPort + "/index.html";

    Logger.warn("port=" + this.serverPort + "  indexHtmlPath=" + indexHtmlPath
      + "\ngameRootPath=" + this.gameRootPath + "  httpGameUrl=" + this.httpGameUrl)
  }

  startHttpServer() {
  	//【5】
    httpServer.enableLog(true);
    httpServer.start(this.serverPort, (err: BusinessError, port: number) => {
      this.serverPort = port;
      if (err) {
        Logger.error("http server error: " + JSON.stringify(err))
      } else {
        this.httpGameUrl = this.host + this.serverPort + "/index.html";
        Logger.info("http 服务启动并加载: " + this.serverPort + "  " + this.httpGameUrl)
        this.controller.loadUrl(this.httpGameUrl)
        KVUtils.putNumber(Constant.KEY_HTTP_PORT, this.serverPort);
      }
    })
  }

  aboutToDisappear(): void {
    httpServer.stop();
    this.controller.deleteJavaScriptRegister(this.GAME_JS_OBJECT);
  }

  build() {
    Column() {
      Web({ src: this.httpGameUrl, controller: this.controller })
        .onInterceptRequest((event) => {
          return this.processRequest(event)//【4】
        })
        .javaScriptProxy({//【2】
          object: this.h5JavaScriptObj,
          name: this.GAME_JS_OBJECT,
          methodList: this.h5JavaScriptObj.getMethodList(),
          controller: this.controller
        })
        .javaScriptAccess(true)
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Black)
    }
    .width('100%')
    .height('100%')
  }

  onBackPress(): boolean | void {
    return DNSDK.onBackPress();
  }

  private processRequest(event: OnInterceptRequestEvent): WebResourceResponse | null {
    const requestUrl = event.request.getRequestUrl();
    const localUrl = this.host + this.serverPort;
    const path = requestUrl.substring(requestUrl.indexOf(localUrl) + localUrl.length)
    const reallyPath = this.gameRootPath + path;

    const url = event.request.getRequestUrl()
    if (url == undefined) {
      return null;
    }

    if (url.startsWith("http://127.0.0.1") || url.startsWith("http://localhost")) {
      if (fs.accessSync(reallyPath)) {
        const st = fs.statSync(reallyPath);
        const buffer = new ArrayBuffer(st.size);
        fs.readSync(fs.openSync(reallyPath).fd, buffer);

        const response = new WebResourceResponse();
        response.setResponseData(buffer);
        response.setResponseEncoding('utf-8');
        response.setResponseMimeType(CommonUtil.getMimeType(reallyPath));
        response.setResponseCode(200);
        return response;
      }
    }

    Logger.error('\t文件不存在 =' + reallyPath);
    return null;
  }
}

跳转到 h5 页面

 router.replaceNamedRoute({
          name: GlobalUrl.ROUTER_NAME_H5_GAME_PAGE,
        }).catch((error: Error) => {
          Logger.error('CustomDialog  pushUrl error =' + JSON.stringify(error));
        });

资源类型匹配

 public getMimeType(path: string): string {
    if (path.endsWith('.png')) {
      return 'image/png';
    }
    if (path.endsWith('.css')) {
      return 'text/css';
    }
    if (path.endsWith('.js')) {
      return 'application/javascript';
    }
    if (path.endsWith('.html')) {
      return 'text/html';
    }
    if (path.endsWith('.xml')) {
      return 'text/xml';
    }
    if (path.endsWith('.java')) {
      return 'text/x-java-source, text/java';
    }
    if (path.endsWith('.gif')) {
      return 'image/gif';
    }
    if (path.endsWith('.jpg') || path.endsWith('.jpeg')) {
      return 'image/jpeg';
    }
    if (path.endsWith('.png')) {
      return 'image/png';
    }
    if (path.endsWith('.svg')) {
      return 'image/svg+xml';
    }
    if (path.endsWith('.mp3')) {
      return 'audio/mpeg';
    }
    if (path.endsWith('.mp4')) {
      return 'video/mp4';
    }
    if (path.endsWith('.zip')) {
      return 'application/octet-stream';
    }
    return 'text/plain';
  }

网站公告

今日签到

点亮在社区的每一天
去签到