網(wǎng)站站外優(yōu)化網(wǎng)絡(luò)公司的推廣
鴻蒙ArkTs如何實現(xiàn)pdf預(yù)覽功能?
- 前言
- PDFKit運(yùn)行示例代碼報錯
- 真機(jī)運(yùn)行
- 先看效果
- 一、預(yù)覽本地pdf文件
- 二、預(yù)覽線上的pdf文件
- 三、預(yù)覽沙箱目錄中pdf的文件(重點(diǎn))
- 效果中的整體代碼
- 總結(jié)
- Harmony OS NEXT版本(接口及解決方案兼容API12版本或以上版本)
前言
在開發(fā)鴻蒙App時,你是否做過pdf預(yù)覽功能。是否也和我一樣碰壁了,來看看我遇到的問題,以及我是如何實現(xiàn)的吧。
PDFKit運(yùn)行示例代碼報錯
the requested module '@hms:officeservice.PdfView' does not provide an export name 'pdfViewManager' which imported by 'xxxx'
真機(jī)運(yùn)行
本來以為用真機(jī)就能運(yùn)行了,沒想到還是報錯
那么下面來看看我是如何實現(xiàn)的吧
先看效果
視頻轉(zhuǎn)完gif,視覺上看起來有點(diǎn)卡,實際運(yùn)行不卡。
一、預(yù)覽本地pdf文件
預(yù)覽本地的pdf文件很簡單,使用Web組件加載即可。
pdf文件目錄:harmonyApp\entry\src\main\resources\rawfile\test.pdf
具體代碼如下:
import web_webview from '@ohos.web.webview';@Entry
@Component
struct Index {webviewController: web_webview.WebviewController = new web_webview.WebviewController();build() {Column() {// src-本地pdf文件Web({ src: $rawfile('test.pdf'), controller: this.webviewController }).layoutWeight(1).domStorageAccess(true)}.height('100%')}
}
二、預(yù)覽線上的pdf文件
這里的線上的pdf文件是指可以在瀏覽器直接打開預(yù)覽的pdf文件,還有一種是在瀏覽器打開是直接進(jìn)入下載的,那么就需要我們進(jìn)一步處理了,第三點(diǎn)有詳解。
這樣的文件預(yù)覽也很簡單,使用Web組件加載即可。
具體代碼如下:
import web_webview from '@ohos.web.webview';@Entry
@Component
struct Index {webviewController: web_webview.WebviewController = new web_webview.WebviewController();build() {Column() {// 線上pdf鏈接Web({ src: 'http://www.cztouch.com/upfiles/soft/testpdf.pdf', controller: this.webviewController }).layoutWeight(1).domStorageAccess(true)}.height('100%')}
}
三、預(yù)覽沙箱目錄中pdf的文件(重點(diǎn))
這種就比較麻煩了,有的pdf鏈接在瀏覽器打開直接跳轉(zhuǎn)下載不會預(yù)覽,那么就需要我們下載到沙箱目錄中,再預(yù)覽沙箱目錄中的pdf文件。
我這里用到了一個pdfviewer工具,可從我的百度網(wǎng)盤免費(fèi)獲取
拿到文件夾后,放在以下目錄:
項目目錄:harmonyApp\entry\src\main\resources\rawfile
具體實現(xiàn)代碼如下:
import router from '@ohos.router';
import web_webview from '@ohos.web.webview';
import { BusinessError, request } from '@kit.BasicServicesKit';
import showToast from '../../common/utils/ToastUtils';
import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { util } from '@kit.ArkTS';interface IBase64 {base64: string;fileName: string;
}@Entry
@Component
struct Index2 {controller: web_webview.WebviewController = new web_webview.WebviewController()// pdf文件路徑@State fileUrl: string = ''// 本地沙箱文件地址@State tempFilePath: string = ''// 是否顯示按鈕@State isShowBtn: boolean = true;build() {Stack() {Column() {// 頁面內(nèi)容Scroll(){Column(){if(this.tempFilePath){if(this.isShowBtn){Button('打開文件').onClick(()=>{this.isShowBtn = false;})}else{Web({ src: $rawfile('pdfviewer/viewer.html'), controller: this.controller }).onProgressChange((event)=>{console.log("newProgress", event?.newProgress)}).domStorageAccess(true) // 設(shè)置是否開啟文檔對象模型存儲接口(DOM Storage API)權(quán)限,默認(rèn)未開啟。.onPageEnd(()=>{let file = this.sandBoxPdfToBase64(this.tempFilePath);this.controller.runJavaScript(`openFile("${file.base64}", "${file.fileName}")`);})}}}.width('100%').height('100%')}.edgeEffect(EdgeEffect.Fade).width('100%').layoutWeight(1).align(Alignment.TopStart)}.height('100%').backgroundColor(Color.White)}}// 沙箱pdf文件轉(zhuǎn)base64方法sandBoxPdfToBase64(url: string) {let file = fs.openSync(url, fs.OpenMode.READ_WRITE); // 打開文件let stat = fs.statSync(url); // 獲取文件狀態(tài)let buf = new ArrayBuffer(stat.size); // 創(chuàng)建一個ArrayBuffer對象let base64 = new util.Base64Helper(); // 實例化Base64Helperlet num = fs.readSync(file.fd, buf); // 讀取文件let data = base64.encodeSync(new Uint8Array(buf.slice(0, num))) // 轉(zhuǎn)換成Uint8Arraylet textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true })let retStr = textDecoder.decodeWithStream(data, { stream: false }); // 可以把Uint8Array轉(zhuǎn)碼成base64let fileName = file.namefs.closeSync(file);return { base64: retStr, fileName: fileName } as IBase64;}// 下載pdf文件,獲取沙箱文件目錄getTempFile(fileUrl:string){let context = getContext(this) as common.UIAbilityContext;const fileFullName = fileUrl.split('/')[fileUrl.split('/').length - 1]let tempFilePath = `${context.filesDir}/${fileFullName}`;//文件如果已經(jīng)存在,就刪除if (fs.accessSync(tempFilePath)) {fs.unlink(tempFilePath)}request.downloadFile(getContext(), { url: fileUrl,filePath: tempFilePath }).then((data: request.DownloadTask) => {let downloadTask: request.DownloadTask = data;let progressCallback = (receivedSize: number, totalSize: number) => {// 這里可以自行編寫下載進(jìn)度條showToast(`下載大小${receivedSize},總大小${totalSize}`);};let completeCallback = ()=>{showToast("下載完畢");this.tempFilePath = tempFilePath;}downloadTask.on('progress', progressCallback);downloadTask.on('complete', completeCallback)}).catch((err: BusinessError) => {console.error(`Failed to request the download. Code: ${err.code}, message: ${err.message}`);})}// 組件生命周期:組件即將出現(xiàn)時回調(diào)該接口aboutToAppear() {console.log('進(jìn)入頁面')// 你的pdf鏈接this.fileUrl = (router.getParams() as Record<string, string>).url || '';this.getTempFile((router.getParams() as Record<string, string>).url as string);}
}
這里附有將pdf文件下載到沙箱目錄代碼,可選擇使用(不必須)。
效果中的整體代碼
import web_webview from '@ohos.web.webview';
import promptAction from '@ohos.promptAction'
import { BusinessError, request } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { util } from '@kit.ArkTS';// pdf頁面tab接口
interface pageTab {name:string;
}interface IBase64 {base64: string;fileName: string;
}/*** pdfPage的ViewModel*/
class PdfPageModel {// 當(dāng)前索引curTabIndex:number = 0;// pdf頁面tabtabList:pageTab[] = [{ name:'預(yù)覽本地PDF文件' },{ name:'預(yù)覽網(wǎng)絡(luò)PDF文件' },{ name:'預(yù)覽沙箱PDF文件' },];// 網(wǎng)絡(luò)文件fileUrl: string = 'http://www.cztouch.com/upfiles/soft/testpdf.pdf'// 本地沙箱文件地址tempFilePath: string = ''constructor() {}// 沙箱pdf文件轉(zhuǎn)base64方法sandBoxPdfToBase64(url: string) {let file = fs.openSync(url, fs.OpenMode.READ_WRITE); // 打開文件let stat = fs.statSync(url); // 獲取文件狀態(tài)let buf = new ArrayBuffer(stat.size); // 創(chuàng)建一個ArrayBuffer對象let base64 = new util.Base64Helper(); // 實例化Base64Helperlet num = fs.readSync(file.fd, buf); // 讀取文件let data = base64.encodeSync(new Uint8Array(buf.slice(0, num))) // 轉(zhuǎn)換成Uint8Arraylet textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true })let retStr = textDecoder.decodeWithStream(data, { stream: false }); // 可以把Uint8Array轉(zhuǎn)碼成base64let fileName = file.namefs.closeSync(file);return { base64: retStr, fileName: fileName } as IBase64;}// 下載pdf文件,獲取沙箱文件目錄getTempFile(fileUrl:string){let context = getContext(this) as common.UIAbilityContext;const fileFullName = fileUrl.split('/')[fileUrl.split('/').length - 1]let tempFilePath = `${context.filesDir}/${fileFullName}`;//文件如果已經(jīng)存在,就刪除if (fs.accessSync(tempFilePath)) {fs.unlink(tempFilePath)}request.downloadFile(getContext(), { url: fileUrl,filePath: tempFilePath }).then((data: request.DownloadTask) => {let downloadTask: request.DownloadTask = data;let progressCallback = (receivedSize: number, totalSize: number) => {// 這里可以自行編寫下載進(jìn)度條// showToast(`下載大小${receivedSize},總大小${totalSize}`);};let completeCallback = ()=>{// showToast("下載完畢");this.tempFilePath = tempFilePath;}downloadTask.on('progress', progressCallback);downloadTask.on('complete', completeCallback)}).catch((err: BusinessError) => {console.error(`Failed to request the download. Code: ${err.code}, message: ${err.message}`);})}// tab切換switchTab(index:number){this.curTabIndex = index;if(index === 2 && !this.tempFilePath){try {promptAction.showDialog({title: '溫馨提示',message: '有些pdf線上鏈接是經(jīng)過第三方加密過的,在瀏覽器訪問時不能直接預(yù)覽,直接走的是下載的pdf文件鏈接,可以采用這種方式,先下載在沙箱目錄中,然后再預(yù)覽沙箱中的pdf文件',buttons: [{text: '知道了',color: '#000000'}]}, (err, data) => {if (err) {console.error('showDialog err: ' + err);return;}console.info('showDialog success callback, click button: ' + data.index);});} catch (error) {console.error(`Failed to show dialog. Code: ${error.code}, message: ${error.message}`);}this.getTempFile(this.fileUrl);}}
}@Entry
@Component
struct PdfPage {webviewController: web_webview.WebviewController = new web_webview.WebviewController();@State vm: PdfPageModel = new PdfPageModel();// 驗證是否選中VerifySelectedFun( curIndex:number , itemIndex:number ):boolean{return curIndex == itemIndex}aboutToAppear(): void {try {promptAction.showDialog({title: '溫馨提示',message: '在模擬器中運(yùn)行,首次加載會出現(xiàn)黑屏,但來回切換幾次tab標(biāo)簽就好了,有條件的建議使用真機(jī)運(yùn)行,不會有這樣的問題',buttons: [{text: '知道了',color: '#000000'}]}, (err, data) => {if (err) {console.error('showDialog err: ' + err);return;}console.info('showDialog success callback, click button: ' + data.index);});} catch (error) {console.error(`Failed to show dialog. Code: ${error.code}, message: ${error.message}`);}}build() {Stack() {Column() {// tab標(biāo)簽條Row(){Scroll(){Row(){ForEach(this.vm.tabList,(item:pageTab,index)=>{Row(){if(this.VerifySelectedFun(this.vm.curTabIndex,index)){Stack(){Row(){}.width(40).height(10).borderRadius(20).offset({y:7}).linearGradient({angle:89.11,colors:[['rgba(255, 255, 255, 0.55)',0.0682],['rgba(217, 217, 217, 0)',1]]})Text(item.name).fontSize(18).fontColor($r('app.color.primary_theme_color')).fontWeight(600).height('100%')}}else{Text(item.name).fontSize(16)// .fontColor($r('app.color.font_color_default')).fontWeight(400)}}.height('100%').justifyContent(FlexAlign.Start).padding({ left: index == 0 ? 0 : 20 }).onClick(()=>{this.vm.switchTab(index)})})}}.edgeEffect(EdgeEffect.Fade).layoutWeight(1).align(Alignment.Center).scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off)}.width('100%').height(50).justifyContent(FlexAlign.Start).padding({left:16,right:16}).backgroundColor(Color.White)// 頁面內(nèi)容Scroll(){Column(){if(this.vm.curTabIndex === 0 ){// web組件加載本地pdf文件Web({ src: $rawfile('Git.pdf'), controller: this.webviewController}).domStorageAccess(true).onProgressChange((event)=>{console.log("newProgress", event?.newProgress)})}else if(this.vm.curTabIndex === 1){// web組件加載網(wǎng)絡(luò)pdf文件Web({ src: 'http://www.cztouch.com/upfiles/soft/testpdf.pdf', controller: this.webviewController }).layoutWeight(1).domStorageAccess(true).onProgressChange((event)=>{console.log("newProgress", event?.newProgress)})}else if(this.vm.curTabIndex === 2){if(this.vm.tempFilePath){Web({ src: $rawfile('pdfviewer/viewer.html'), controller: this.webviewController }).onProgressChange((event)=>{console.log("newProgress", event?.newProgress)}).domStorageAccess(true) // 設(shè)置是否開啟文檔對象模型存儲接口(DOM Storage API)權(quán)限,默認(rèn)未開啟。.onPageEnd(()=>{let file = this.vm.sandBoxPdfToBase64(this.vm.tempFilePath);this.webviewController.runJavaScript(`openFile("${file.base64}", "${file.fileName}")`);})}}}.padding({ left: 16, right: 16, bottom: 16 })}.edgeEffect(EdgeEffect.Fade).width('100%').layoutWeight(1).align(Alignment.TopStart)}.height('100%').backgroundColor('#F5F5F5').padding({ bottom: 16 })}}
}
總結(jié)
總體來說就是使用Web組件加載pdf文件,在模擬器中運(yùn)行,首次運(yùn)行會黑屏,不過來回切換一下tab頁就好了,真機(jī)運(yùn)行沒有問題。
為啥要存到沙箱中再預(yù)覽,豈不是多此一舉?
當(dāng)然不是,因為有的pdf文件是通過第三方加密過的,在瀏覽器打開鏈接時,是不能直接預(yù)覽的,而是直接走下載了。這時,就需要先存到沙箱目錄中再預(yù)覽。
有需要的朋友,拿走不謝,求贊,求贊,求贊~
關(guān)注我不迷路,不定時分享鴻蒙難點(diǎn)亮點(diǎn)