写点什么

鸿蒙网络编程系列 22-Web 组件文件上传示例

作者:长弓三石
  • 2024-10-22
    广东
  • 本文字数:4274 字

    阅读完需:约 14 分钟

1. web 组件文件上传功能简介

鸿蒙的 web 组件可以加载网页,如果网页本身具备文件上传功能的话就比较尴尬了,因为 html 上传文件时,允许用户选择本地文件,但是鸿蒙因为安全性的考虑,只允许操作沙箱中的文件,所以在 web 组件中的上传功能本身无法直接使用。如果一定要使用的话,就要另辟蹊径,既然不允许选择本地文件,那么,我们给它提供沙箱中的文件就好了,web 组件提供了 onShowFileSelector 事件,在处理具有“文件”输入类型的 HTML 表单时,如果用户按下“选择文件”按钮,会触发该事件,该事件的定义如下:


onShowFileSelector(callback: (event?: { result: FileSelectorResult, fileSelector: FileSelectorParam }) => boolean)
复制代码


其中,参数 result 为 FileSelectorResult 类型,是重点要处理的对象,它提供了 handleFileList 方法,可以通知 web 组件选择的沙箱文件,定义如下:


handleFileList(fileList: Array<string>): void
复制代码


参数 fileList 就是需要进行操作的文件列表。


onShowFileSelector 其他的参数说明可以参考相关文档。

2. web 组件文件上传文件示例

本示例运行后的界面如下所示:



该示例允许上传三种文件类型,第一种是资源文件,也就是在开发期间通过 rawfile 添加的文件,第二种是图片文件,会打开图片选择器让用户选择图片,第三种是普通文件,允许用户选择任意类型的文件。


下面详细介绍创建该应用的步骤。


步骤 1:创建 Empty Ability 项目。


步骤 2:在 module.json5 配置文件加上对权限的声明:


"requestPermissions": [      {        "name": "ohos.permission.INTERNET"      }    ]
复制代码


这里添加了获取互联网信息的权限。


步骤 3:资源目录添加 demo.txt 文件,示意图如下:



步骤 4:在 Index.ets 文件里添加如下的代码:


import fs from '@ohos.file.fs';import picker from '@ohos.file.picker';import web_webview from '@ohos.web.webview'
@Entry@Componentstruct Index { //要加载的网址 @State webUrl: string = "http://192.168.100.102:8081/index" //文件的沙箱路径 sandboxFilePath: string = ""
//上传文件的类型,0:资源文件;1:图像文件;2:普通文件,默认图像文件 uploadFileType = 1
scroller: Scroller = new Scroller() controller: web_webview.WebviewController = new web_webview.WebviewController()
build() { Row() { Column() { Text("Web组件文件上传示例") .fontSize(14) .fontWeight(FontWeight.Bold) .width('100%') .textAlign(TextAlign.Center) .padding(10)
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { Text("网址:") .fontSize(14) .width(50) .flexGrow(0)
TextInput({ text: this.webUrl }) .onChange((value) => { this.webUrl = value }) .width(110) .fontSize(11) .flexGrow(1)
Button("加载") .onClick(() => { this.controller.loadUrl(this.webUrl); }) .width(60) .fontSize(14) .flexGrow(0) } .width('100%') .padding(5)
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { Text("上传文件类型:") .fontSize(14) .width(150) .flexGrow(0) } .width('100%') .padding(5)
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { Column() { Radio({ value: '', group: 'radioGroup' }).checked(this.uploadFileType === 0) .height(30) .width(100) .onChange((isChecked: boolean) => { if (isChecked) { this.uploadFileType = 0 } }) Text('资源文件') }
Column() { Radio({ value: '', group: 'radioGroup' }).checked(this.uploadFileType === 1) .height(30) .width(100) .onChange((isChecked: boolean) => { if (isChecked) { this.uploadFileType = 1 } }) Text('图片文件') }
Column() { Radio({ value: '', group: 'radioGroup' }).checked(this.uploadFileType === 2) .height(30) .width(100) .onChange((isChecked: boolean) => { if (isChecked) { this.uploadFileType = 2 } }) Text('普通文件') } } .width('100%') .padding(5)
Scroll(this.scroller) { Web({ src: this.webUrl, controller: this.controller }) .padding(10) .width('100%') .textZoomRatio(150) .backgroundColor(0xeeeeee) .onShowFileSelector((event) => { this.selectFile().then((selected) => { if (selected) { let fileList: Array<string> = [this.sandboxFilePath,] event.result.handleFileList(fileList) } }) return true }) } .align(Alignment.Top) .backgroundColor(0xeeeeee) .height(300) .flexGrow(1) .scrollable(ScrollDirection.Vertical) .scrollBar(BarState.On) .scrollBarWidth(20) } .width('100%') .justifyContent(FlexAlign.Start) .height('100%') } .height('100%') }
//选择上传的文件 async selectFile(): Promise<boolean> { //资源文件中的demo.txt if (this.uploadFileType == 0) { return this.copyResFile2Sandbox("demo.txt") } else if (this.uploadFileType == 1) {//选择图像文件 let imgPicker = new picker.PhotoViewPicker(); let result = await imgPicker.select(); if (result.photoUris.length > 0) { return this.copySelFile2Sandbox(result.photoUris[0]) } return false } else {//选择任意文件 let filePicker = new picker.DocumentViewPicker(); let result = await filePicker.select(); if (result.length > 0) { return this.copySelFile2Sandbox(result[0]) } return false } }
//复制资源文件到沙箱 async copyResFile2Sandbox(resFile: string): Promise<boolean> { let context = getContext(this) //计划复制到的目标路径 let realUri = context.cacheDir + "/" + resFile let rawFd = await context.resourceManager.getRawFd(resFile)
//复制资源文件到沙箱cache文件夹 try { fs.copyFileSync(rawFd.fd, realUri) this.sandboxFilePath = realUri return true } catch (err) { console.error(err.message) } return false }
//复制选中文件到沙箱 copySelFile2Sandbox(selectFile: string): boolean { let context = getContext(this) let segments = selectFile.split('/') //文件名称 let fileName = segments[segments.length-1] //计划复制到的目标路径 let realUri = context.cacheDir + "/" + fileName //复制选择的文件到沙箱cache文件夹 try { let file = fs.openSync(selectFile); fs.copyFileSync(file.fd, realUri) this.sandboxFilePath = realUri return true } catch (err) { console.error(err.message) } return false }}
复制代码


步骤 5:编译运行,可以使用模拟器或者真机。


步骤 6:输入包含上传功能的网页,然后单击“加载”按钮,加载网页。


步骤 7:选择“资源文件”类型,然后单击 web 组件中的“选择文件”按钮,会选择资源文件中的 demo.txt 文件:



步骤 8:单击“上传文件”按钮,会上传到服务端,在服务端可以看到上传的文件。


步骤 9:选择“图片文件”类型,然后单击 web 组件中的“选择文件”按钮,会弹出图片选择器,然后选择其中一张图片:



步骤 10:单击“完成”按钮,然后单击“上传文件”按钮,会上传到服务端。



步骤 11:选择“普通文件”类型,然后单击 web 组件中的“选择文件”按钮,会弹出文件选择器,然后选择任意文件:



步骤 12:同样,单击“上传文件”按钮,会上传到服务端。


这样就完成了多种文件类型的 web 组件上传。

3. 上传功能分析

在这三种方式中,本质上都是把文件复制到沙箱,然后把沙箱文件路径给 web 组件,其中比较复杂的是第一种,就是资源文件。资源文件和其他文件不太一样,不能直接复制到沙箱,而是先通过 resourceManager 得到资源文件 RawFd,然后得到 fd,最后使用该 fd 进行复制,代码如下:


  //复制资源文件到沙箱  async copyResFile2Sandbox(resFile: string): Promise<boolean> {    let context = getContext(this)    //计划复制到的目标路径    let realUri = context.cacheDir + "/" + resFile    let rawFd = await context.resourceManager.getRawFd(resFile)
//复制资源文件到沙箱cache文件夹 try { fs.copyFileSync(rawFd.fd, realUri) this.sandboxFilePath = realUri return true } catch (err) { console.error(err.message) } return false }
复制代码


另外两种都是复制普通文件到沙箱的方式,在前述文章中都多次使用,就不赘述了。


(本文作者原创,除非明确授权禁止转载)


本文源码地址:


https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/web/UploadInWeb


本系列源码地址:


https://gitee.com/zl3624/harmonyos_network_samples


发布于: 刚刚阅读数: 4
用户头像

长弓三石

关注

还未添加个人签名 2024-10-16 加入

二十多年软件开发经验的软件架构师,华为HDE、华为云HCDE、仓颉语言CLD、CCS,著有《仓颉语言网络编程》、《仓颉语言元编程》、《仓颉语言实战》、《鲲鹏架构入门与实战》等书籍,清华大学出版社出版。

评论

发布
暂无评论
鸿蒙网络编程系列22-Web组件文件上传示例_DevEco Studio_长弓三石_InfoQ写作社区