1、HarmonyOS Web組件本地資源跨域問題?
關于資源跨域問題的解決,可以參考以下官網文檔:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/web-cross-origin-V5
方法一
為了使Web組件能夠成功訪問跨域資源,開發者應采用http或https等協議,替代原先使用的file或resource協議進行加載。其中,替代的url域名為自定義構造的僅供個人或者組織使用的域名,以避免與互聯網上實際存在的域名產生沖突。同時,開發者需利用Web組件的onInterceptRequest方法,對本地資源進行攔截和相應的替換。
以下結合示例說明如何解決本地資源跨域訪問失敗的問題。其中,index.html和js/script.js置于工程中的rawfile目錄下。如果使用resource協議訪問index.html,js/script.js將因跨域而被攔截,無法加載。在示例中,使用https://www.example.com/域名替換了原本的resource協議,同時利用onInterceptRequest接口替換資源,使得js/script.js可以成功加載,從而解決了跨域攔截的問題。
// main/ets/pages/Index.ets
import { webview } from '@kit.ArkWeb';@Entry
@Component
struct Index {@State message: string = 'Hello World';webviewController: webview.WebviewController = new webview.WebviewController();// 構造域名和本地文件的映射表schemeMap = new Map([["https://www.example.com/index.html", "index.html"],["https://www.example.com/js/script.js", "js/script.js"],])// 構造本地文件和構造返回的格式mimeTypemimeTypeMap = new Map([["index.html", 'text/html'],["js/script.js", "text/javascript"]])build() {Row() {Column() {// 針對本地index.html,使用http或者https協議代替file協議或者resource協議,并且構造一個屬于自己的域名。// 本例中構造www.example.com為例。Web({ src: "https://www.example.com/index.html", controller: this.webviewController }).javaScriptAccess(true).fileAccess(true).domStorageAccess(true).geolocationAccess(true).width("100%").height("100%").onInterceptRequest((event) => {if (!event) {return;}// 此處匹配自己想要加載的本地離線資源,進行資源攔截替換,繞過跨域if (this.schemeMap.has(event.request.getRequestUrl())) {let rawfileName: string = this.schemeMap.get(event.request.getRequestUrl())!;let mimeType = this.mimeTypeMap.get(rawfileName);if (typeof mimeType === 'string') {let response = new WebResourceResponse();// 構造響應數據,如果本地文件在rawfile下,可以通過如下方式設置response.setResponseData($rawfile(rawfileName));response.setResponseEncoding('utf-8');response.setResponseMimeType(mimeType);response.setResponseCode(200);response.setReasonMessage('OK');response.setResponseIsReady(true);return response;}}return null;})}.width('100%')}.height('100%')}
}
<!-- main/resources/rawfile/index.html -->
<html>
<head><meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<script crossorigin src="./js/script.js"></script>
</body>
</html>
// main/resources/rawfile/js/script.js
const body = document.body;
const element = document.createElement('div');
element.textContent = 'success';
body.appendChild(element);
方法二
通過setPathAllowingUniversalAccess設置一個路徑列表。當使用file協議訪問該列表中的資源時,允許進行跨域訪問本地文件。此外,一旦設置了路徑列表,file協議將僅限于訪問列表內的資源(此時,fileAccess的行為將會被此接口行為覆蓋)。路徑列表中的路徑必須符合以下任一路徑格式:
- 應用文件目錄通過Context.filesDir獲取,其子目錄示例如下:
- /data/storage/el2/base/files/example
- /data/storage/el2/base/haps/entry/files/example
- 應用資源目錄通過Context.resourceDir獲取,其子目錄示例如下:
- /data/storage/el1/bundle/entry/resource/resfile
- /data/storage/el1/bundle/entry/resource/resfile/example
當路徑列表中的任一路徑不滿足上述條件時,系統將拋出異常碼401,并判定路徑列表設置失敗。若設置的路徑列表為空,file協議的可訪問范圍將遵循fileAccess的規則,具體示例如下。
// main/ets/pages/Index.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';@Entry
@Component
struct WebComponent {controller: WebviewController = new webview.WebviewController();build() {Row() {Web({ src: "", controller: this.controller }).onControllerAttached(() => {try {// 設置允許可以跨域訪問的路徑列表this.controller.setPathAllowingUniversalAccess([getContext().resourceDir,getContext().filesDir + "/example"])this.controller.loadUrl("file://" + getContext().resourceDir + "/index.html")} catch (error) {console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);}}).javaScriptAccess(true).fileAccess(true).domStorageAccess(true)}}
}
<!-- main/resource/rawfile/index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>Demo</title><meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover"><script>function getFile() {var file = "file:///data/storage/el1/bundle/entry/resources/resfile/js/script.js";// 使用file協議通過XMLHttpRequest跨域訪問本地js文件。var xmlHttpReq = new XMLHttpRequest();xmlHttpReq.onreadystatechange = function(){console.log("readyState:" + xmlHttpReq.readyState);console.log("status:" + xmlHttpReq.status);if(xmlHttpReq.readyState == 4){if (xmlHttpReq.status == 200) {// 如果ets側正確設置路徑列表,則此處能正常獲取資源const element = document.getElementById('text');element.textContent = "load " + file + " success";} else {// 如果ets側不設置路徑列表,則此處會觸發CORS跨域檢查錯誤const element = document.getElementById('text');element.textContent = "load " + file + " failed";}}}xmlHttpReq.open("GET", file);xmlHttpReq.send(null);}</script>
</head><body>
<div class="page"><button id="example" onclick="getFile()">stealFile</button>
</div>
<div id="text"></div>
</body></html>
// main/resources/rawfile/js/script.js
const body = document.body;
const element = document.createElement('div');
element.textContent = 'success';
body.appendChild(element);
2、HarmonyOS web里需要實現長按彈出菜單保存圖片,有類似getHitTestResult的方法嗎?
可以參考下web組件中的onContextMenuShow事件,長按特定元素(例如圖片,鏈接)或鼠標右鍵,跳出菜單。具體參考下面文檔:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-basic-components-web-V5#oncontextmenushow9
暫時無法控制長按彈框的條件,可以嘗試下從h5側通過控制事件和CSS方式控制選擇框
3、HarmonyOS web攔截應用跳轉和自定義請求響應對應demo?
web攔截和自定義請求響應,可以參考官方文檔:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/web-resource-interception-request-mgmt-V5
Web組件支持在應用攔截到頁面請求后自定義響應請求能力。開發者通過onInterceptRequest()接口來實現自定義資源請求響應 。自定義請求能力可以用于開發者自定義Web頁面響應、自定義文件資源響應等場景。
Web網頁上發起資源加載請求,應用層收到資源請求消息。應用層構造本地資源響應消息發送給Web內核。Web內核解析應用層響應信息,根據此響應信息進行頁面資源加載。
4、HarmonyOS 如何禁止單個page頁面錄屏截屏 ;禁止某個dialog 錄屏截屏 如dialog 安全密碼鍵盤?
現在需要在某些page 頁面、dialog,不讓錄屏和截屏
可以通過setWindowPrivacyMode設置窗口為隱私模式后,截屏錄屏或分享屏幕,屏幕會顯示灰色蒙層(隱私窗口不允許這些操作)
可參考:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-window-V5#setwindowprivacymode9
setWindowPrivacyMode
setWindowPrivacyMode(isPrivacyMode: boolean, callback: AsyncCallback): void
設置窗口是否為隱私模式,使用callback異步回調。設置為隱私模式的窗口,窗口內容將無法被截屏或錄屏。此接口可用于禁止截屏/錄屏的場景。
#### 5、HarmonyOS 如何獲取 base64圖片的圖片原始寬高? 圖片驗證碼場景,服務端返回兩張base64 格式圖片 背景圖與驗證圖,需要根據背景圖、原圖大小計算縮放系數,從而計算驗證圖的大小與定位信息使用Image Kit的能力,參考文檔:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-image-V5#imageinfo
demo:
import util from '@ohos.util';
import { image } from '@kit.ImageKit';
@Entry
@Component
struct Index {@State message: string = 'getImage';@StorageLink('test') test: object = new Object;@State imageBase64: string ='iVBORw0KGgoAAAANSUhEUgAAADwAAAAUCAYAAADRA14pAAABN0lEQVR42mP4P8IAAy0Mjf6xAYxHnIcHo6cZaOlZYj38VbESjIech5E9SayHYZ5FxnT1cL7uFwxMbt4lxtPYPElLjzNg8ywhMWp6GOZBeiVzDA/jinFySmZSkzUpHn5oLosXk+1hYj2NXliRUnjh8hy5MYzP0wzEeIzUvEyNGCY3WZMUw5Qm61EPjzQPkwIGjYfp4VlsnianIULIs3gbHvT2LLZWFzVLZ7xNS3p7lBqAGM+CPZy6o+w/DGfvrv5ffagTjtuOT/4/8cxcOF50Zc3/5dc3wvHeh0fh+PDjk/8vv74Bx/c+PPz/8utrOP7559fg8LD/uqT/A4GpHdB7Q/XBmFBAMyBLPv70DCWWTjw7h2L42pvbUCxGdlTPqRkoji7Y24DiqdCN6f8HKnCRMcNA5bmBCmgACwohlRAJ3H4AAAAASUVORK5CYII='@State pixelMap: image.PixelMap | undefined = undefined;build() {Column() {Text(this.message).id('HelloWorld').fontSize(50).fontWeight(FontWeight.Bold).onClick(async () => {let helper = new util.Base64Helper();let buffer: ArrayBuffer = helper.decodeSync(this.imageBase64, util.Type.MIME).buffer as ArrayBuffer;let imageSource = image.createImageSource(buffer);let opts: image.DecodingOptions = { editable: true };this.pixelMap = await imageSource.createPixelMap(opts);this.pixelMap.getImageInfo().then((imageInfo : image.ImageInfo) => {if (imageInfo == undefined) {console.error(`Failed to obtain the image pixel map information.`);}let wit = imageInfo.size.width;let hig = imageInfo.size.height;console.log(`Succeeded in obtaining the image pixel map information., ${JSON.stringify(wit)}, ${JSON.stringify(hig)}`);})})}.height('100%').width('100%')}
}