HarmonyOS 鸿蒙Next H5页面ArkWeb嵌入应用实现长按图片下载到相册功能示例

发布于 1周前 作者 sinazl 来自 鸿蒙OS

HarmonyOS 鸿蒙Next H5页面ArkWeb嵌入应用实现长按图片下载到相册功能示例 我有个H5页面,通过ArkWeb嵌入在应用中,页面里可以预览新闻图片,需要支持长按图片下载图片到相册的功能,请问有可以参考的示例吗?

8 回复

以下代码,可以在web长按,弹框提示下载,但是还有些粗糙,可以先参考下:

"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  },
  {
    "name": "ohos.permission.WRITE_IMAGEVIDEO",
    "reason": "$string:EntryAbility_desc",
    "usedScene": {
      "abilities": [
        "FormAbility"
      ],
      "when": "inuse"
    }
  }
]

index.ets

// xxx.ets
import { webview } from '@kit.ArkWeb';
import { http } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { ResponseCode } from '@ohos.net.http';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import fs from '@ohos.file.fs';
import { Permissions } from '@kit.AbilityKit';
import { abilityAccessCtrl } from '@kit.AbilityKit';

const TAG = 'ContextMenu';

@Entry
@Component
struct WebComponent {
  controller: webview.WebviewController = new webview.WebviewController();
  private result: WebContextMenuResult | undefined = undefined;
  @State linkUrl: string = '';
  @State offsetX: number = 0;
  @State offsetY: number = 0;
  @State showMenu: boolean = false;
  @State src: string = '';

  @Builder
  //构建自定义菜单及触发功能接口
  MenuBuilder() {
    //以垂直列表形式显示的菜单。
    Menu() {
      //展示菜单Menu中具体的item菜单项。
      MenuItem({
        content: '保存至相册',
      })
        .width(100)
        .height(50)
        .onClick(() => {
          http.createHttp().request(this.src,
            {
              method: http.RequestMethod.GET,
              connectTimeout: 60000,
              readTimeout: 60000
            },
            async (error: BusinessError, data: http.HttpResponse) => {
              if (error) {
                console.error(`http reqeust failed with. Code: ${error.code}, message: ${error.message}`);
              } else {
                if (ResponseCode.ResponseCode.OK === data.responseCode) {
                  let imageBuffer: ArrayBuffer = data.result as ArrayBuffer;
                  try {
                    const permissions: Array<Permissions> = ['ohos.permission.WRITE_IMAGEVIDEO'];
                    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
                    // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
                    await atManager.requestPermissionsFromUser(getContext(this), permissions)
                    // 获取相册路径
                    const context = getContext(this);
                    let helper = photoAccessHelper.getPhotoAccessHelper(context);
                    let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg')
                    let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
                    // 写入文件
                    await fs.write(file.fd, imageBuffer);
                    // 关闭文件
                    await fs.close(file.fd);
                    this.showMenu = false;

                  } catch (error) {
                    this.showMenu = false;
                    console.error("error is " + JSON.stringify(error))
                  }
                } else {
                  this.showMenu = false;
                  console.error('error occurred when image downloaded!')
                }
              }
            })
        })
    .width(150)
    .height(60)
  }

  build() {
    Column() {
      Web({ src: $rawfile("index.html"), controller: this.controller })//触发自定义弹窗
        .onContextMenuShow((event) => {
          if (event) {
            this.result = event.result
            this.src = event.param.getSourceUrl()
            console.info("x coord = " + event.param.x());
            console.info("link url = " + event.param.getLinkUrl());
            // 判断是否是图片
            console.info("src url = " + event.param.getSourceUrl());
            console.info("is image = " + event.param.existsImageContents());
            this.linkUrl = event.param.getLinkUrl();
          }
          console.info(TAG, `x: ${this.offsetX}, y: ${this.offsetY}`);
          this.showMenu = true;
          return true;
        })
        .bindPopup(this.showMenu,
          {
            builder: this.MenuBuilder(),
            enableArrow: false,
            placement: Placement.LeftTop,
            mask: false,
            onStateChange: (e) => {
              if (!e.isVisible) {
                this.showMenu = false;
                this.result!.closeContextMenu();
              }
            }
          })
        .draggable(false)
    }
  }
}

rawfile目录下index.html

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<body>
<h1>onContextMenuShow</h1>
<a href="http://www.example.com" style="font-size:27px">链接www.example.com</a>
<div><img src="https://res1.vmallres.com/pimages//uomcdn/CN/pms/202404/gbom/6942103121098/800_800_E02C87D685D08071877D20E410E8A04Emp.png"></div>
<p>选中文字鼠标右键弹出菜单</p>
</body>
</html>

这样,在web中,可以长按图片,弹窗提示下载,会获取到图片url,拿到图片url,通过http请求,将图片下载至相册。

更多关于HarmonyOS 鸿蒙Next H5页面ArkWeb嵌入应用实现长按图片下载到相册功能示例的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


向应用市场申请ohos.permission.WRITE_IMAGEVIDEO权限,估计会有些困难,可以讨论使用savebutton进行下载,替换原有下载逻辑:

可以通过showAssetsCreationDialog这个接口,在用户同意保存后,返回已创建并授予保存权限的uri列表,然后可使用该uri写入图片/视频,3楼的代码可通过showAssetsCreationDialog稍微改造下,保存代码demo如下:

let imageBuffer: ArrayBuffer = data.result as ArrayBuffer;
try {
  // 获取需要保存到媒体库的位于应用沙箱的图片/视频uri
  let srcFileUris: Array<string> = [
    this.src
  ];
  let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
    {
      title: 'test2', // 可选
      fileNameExtension: 'jpg',
      photoType: photoAccessHelper.PhotoType.IMAGE,
      subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // 可选
    }
  ];
  let context = getContext();
  let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
  let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
  console.info('showAssetsCreationDialog success, data is ' + desFileUris);
  try {
    let file = await fs.open(desFileUris[0], fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
    // 写入文件
    await fs.write(file.fd, imageBuffer);
    // 关闭文件
    await fs.close(file.fd);
  } catch (e) {
    console.log(JSON.stringify(e))
  }
} catch (err) {
  console.error('showAssetsCreationDialog failed, errCode is ' + err.code + ', errMsg is ' + err.message);
}
您好, H5页面可以点击一张图片下载并保存到相册,通过photoAccessHelper来实现保存的逻辑。 您可以参考这边的codelab使用PhotoViewPicker保存图片到图库,参考链接:

[https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_NEXT-FilesManger](https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_NEXT-FilesManger)

注意:

使用photoAccessHelper获取WRITE_IMAGEVIDEO权限,

需要走ACL申请:[https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/restricted-permissions-V5](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/restricted-permissions-V5)。

使用ACL的签名配置指导:[https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-signing-0000001587684945-V5#section157591551175916](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-signing-0000001587684945-V5#section157591551175916)。

同时需要申请网络访问权限ohos.permission.INTERNET

可以参考Demo:

```javascript
import { photoAccessHelper } from '[@kit](/user/kit).MediaLibraryKit';
import { webview } from '[@kit](/user/kit).ArkWeb';
import { fileIo as fs } from '[@kit](/user/kit).CoreFileKit';
import { buffer } from '[@kit](/user/kit).ArkTS';

[@Entry](/user/Entry)
[@Component](/user/Component)
struct main {
  private webviewController: webview.WebviewController = new webview.WebviewController();
  async saveBase64Image(base64Str:string)
  {
    try {
      const context = getContext(this);
      let helper = photoAccessHelper.getPhotoAccessHelper(context);
      let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'png')
      let imageStr = base64Str.split(',')[1]; //打开文件
      let file = fs.openSync(uri, fs.OpenMode.READ_WRITE); //base64字符串转成
      const decodeBuffer = buffer.from(imageStr, 'base64').buffer; //写入文件
      fs.writeSync(file.fd, decodeBuffer); //关闭文件
      fs.closeSync(file);
    } catch (e) {
      console.error(e);
    }
  }

  build() {
    Column() {
      Web({ src: "https://k.sina.cn/article_2688117493_pa0396af5027018z90.html", controller: this.webviewController })
        .onContextMenuShow((event) => {
          if (event) {
            let mediaType = event.param.getMediaType()
            let hitValue = this.webviewController.getHitTestValue()
            let imgBase64 = hitValue.extra
            //长按的是图片,且图片是base64的,因getSourceUrl没取到,用extra取到了
            if (mediaType === ContextMenuMediaType.Image && imgBase64.startsWith("data:image")) {
              let dialogData: AlertDialogParamWithOptions = {
                message: $r('app.string.text_tips_title'),
                buttons: [{
                  value: $r('app.string.tips_save_pic'),
                  action: () => {}
                }],
                buttonDirection: DialogButtonDirection.VERTICAL
              }
              AlertDialog.show(dialogData)
              this.saveBase64Image(imgBase64)
            }
          }
          return false
        })
    }
  }
}

主要分两步来实现:

第一步长按图片获取url,第二步下载网络图片到手机本地

.Web组件事件onContextMenuShow实现长按特定元素(例如图片,链接)或鼠标右键,跳出菜单。在回调函数参数param属性方法existsImageContents判断是否存在图像内容,getSourceUrl可以获取当前图片的Url。

具体可参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-basic-components-web-V5

WebView下载网络图片到手机本地,具体可参考文档的“下载网络资源文件至应用文件目录”章节:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/app-file-upload-download-V5

你好,你给的解决方案只涉及获取网络图片url,我现在是想下载照片到我的图库里,

你好,楼下提供了基于base64下载的网络图片的demo可以参考下哈,

在HarmonyOS鸿蒙系统中,若想在Next H5页面的ArkWeb嵌入应用中实现长按图片下载到相册功能,可以通过以下步骤进行:

首先,确保你的ArkWeb容器已正确集成到鸿蒙应用中,并且H5页面已能够正常显示。接下来,在H5页面中,使用JavaScript监听图片的长按事件。这通常可以通过绑定contextmenu事件或者自定义长按检测逻辑(如使用mousedownmouseup事件结合时间差判断)来实现。

当检测到长按事件后,触发一个下载图片的流程。由于鸿蒙系统对文件系统的访问有限制,你需要通过ArkTS(Ark Runtime的TypeScript扩展)或Java(如果是原生模块)与鸿蒙的媒体库进行交互,以实现图片的保存功能。这通常涉及到调用鸿蒙提供的API来请求存储权限,并将图片数据写入到相册中。

在ArkWeb中,你可能需要通过某种形式的通信机制(如postMessage)将长按事件和相关图片信息传递给ArkTS或原生模块,由其处理具体的保存逻辑。

需要注意的是,鸿蒙系统的API和权限管理可能与Android或iOS有所不同,因此实现时需要参考鸿蒙的官方文档和API指南。

如果问题依旧没法解决请联系官网客服,官网地址是 https://www.itying.com/category-93-b0.html

回到顶部