UniApp 实现pdf上传和预览

一、上传

1、html

<template><button @click="takeFile">pdf上传</button>
</template>

2、JS

takeFile() {// #ifdef H5// H5端使用input方式选择文件const input = document.createElement('input');input.type = 'file';input.accept = '.pdf';input.onchange = (e) => {if (e.target.files && e.target.files[0]) {this.handleSelectedFile(e.target.files[0]);}};input.click();// #endif// #ifdef MP-WEIXIN// 微信小程序选择文件wx.chooseMessageFile({count: 1,type: 'file',extension: ['.pdf'],success: (res) => {if (res.tempFiles && res.tempFiles[0]) {this.handleSelectedFile(res.tempFiles[0]);}},fail: (err) => {console.error('选择文件失败:', err);this.showToast('选择文件失败,请重试');}});// #endif// #ifdef APP-PLUSif (plus.os.name === 'Android') {this.selectPdfAndroid();} else {this.selectPdfIOS();}// #endif},// Android端使用Intent选择PDFselectPdfAndroid() {try {const Intent = plus.android.importClass('android.content.Intent');const Activity = plus.android.runtimeMainActivity();const intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType('application/pdf');intent.addCategory(Intent.CATEGORY_OPENABLE);Activity.startActivityForResult(intent, 1001);Activity.onActivityResult = (requestCode, resultCode, data) => {if (requestCode === 1001 && resultCode === Activity.RESULT_OK && data) {const uri = data.getData();this.handleAndroidUri(uri);}};} catch (error) {console.log('Android Intent失败:', error);this.showToast('当前设备不支持文件选择');}},// 处理Android URI - 简化版本handleAndroidUri(uri) {try {const uriString = uri.toString();console.log('选择的文件URI:', uriString);// 简化处理:生成默认文件名,直接上传const timestamp = Date.now();const fileName = `document_${timestamp}.pdf`;console.log('准备上传文件:', { fileName, uriString });// 直接使用URI进行上传,不获取详细信息this.selectedFile = {path: uriString,name: fileName,size: 0 // 不获取大小,让服务器端验证};this.uploadPdf();} catch (error) {console.log('处理URI失败:', error);this.showToast('文件处理失败,请重试');}},// iOS端选择PDFselectPdfIOS() {// iOS可以使用plus.gallery,设置过滤器plus.gallery.pick((path) => {const fileName = path.split('/').pop() || 'document.pdf';if (fileName.toLowerCase().endsWith('.pdf')) {this.selectedFile = {path: path,name: fileName,size: 0};this.uploadPdf();} else {this.showToast('请选择PDF文件');}},(error) => {console.log('iOS选择失败:', error);this.showToast('文件选择失败');},{filter: 'file'});},/*** 处理选中的文件*/handleSelectedFile(file) {// 验证文件类型if (!this.isPdfFile(file)) {this.showToast('请选择PDF格式的文件');return;}// 验证文件大小 (5MB)if (file.size > 5 * 1024 * 1024) {this.showToast('文件大小不能超过5MB');return;}console.log('pdf', file)this.selectedFile = file;// this.uploadProgress = 0;this.uploadPdf();},/*** 验证是否为PDF文件*/isPdfFile(file) {// #ifdef H5return file.type === 'application/pdf' || file.name.endsWith('.pdf');// #endif// #ifdef MP-WEIXIN || APP-PLUSreturn file.name.endsWith('.pdf');// #endif},/*** 显示提示消息*/showToast(message) {uni.showToast({title: message,icon: 'none'})}

3、打包配置

  /* android打包配置 */"android" : {"permissions" : ["<uses-feature android:name=\"android.hardware.camera\"/>","<uses-feature android:name=\"android.hardware.camera.autofocus\"/>","<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>","<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>","<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.CALL_PHONE\"/>","<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>","<uses-permission android:name=\"android.permission.INTERNET\"/>","<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>","<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>","<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>","<uses-permission android:name=\"android.permission.READ_LOGS\"/>","<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>","<uses-permission android:name=\"android.permission.VIBRATE\"/>","<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>","<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>","<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>","<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>","<uses-permission android:name=\"android.permission.RECORD_AUDIO\" />","<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>","<uses-permission android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"/>","<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>"],"abiFilters" : []},/* ios打包配置 */"ios" : {"idfa" : false,"privacyDescription" : {"NSMicrophoneUsageDescription" : "用户上传视频时需使用音频信息","NSSpeechRecognitionUsageDescription" : "请允许使用您的语音识别,以便更好的体验该应用","NSPhotoLibraryUsageDescription" : "需要用与评论上传,头像上传功能","NSPhotoLibraryAddUsageDescription" : "保存图片到本地","NSFaceIDUsageDescription" : "使用面部识别进行登录","NSCameraUsageDescription" : "需要用与扫描二维码和图片拍摄","NSLocationWhenInUseUsageDescription" : "获得地理位置,为您推荐最近的好友,允许吗?","NSLocationAlwaysUsageDescription" : "需要您同意才能获得您的地理位置,允许吗?","NSLocationAlwaysAndWhenInUseUsageDescription" : "需要一直获取您的地理位置信息,允许吗?"},"dSYMs" : false},

二、预览

1、预览组件

<template><view class="container"><!-- 导航栏 --><!-- <view class="navbar"><view class="back-btn" @click="handleBack"><uni-icons type="left" size="24" color="#333"></uni-icons></view><view class="title">{{ title || 'PDF预览' }}</view><view class="right-space"></view></view> --><!-- PDF      web-view组件:- 用于加载PDF预览页面- 注意:src需要在manifest.json中配置web-view的域名白名单--><web-view :src="webviewUrl" @message="handleWebviewMessage" class="webview":webview-styles="webviewStyles"></web-view><!-- 加载提示 --><!-- <view v-if="loading" class="loading-mask"><view class="loading-container"><uni-loading type="spinner" size="24"></uni-loading><view class="loading-text">加载中...</view></view></view> --></view>
</template><script>
export default {data() {return {title: "", // PDF标题pdfUrl: "", // PDF文件地址webviewUrl: "", // web-view加载的URLloading: true, // 加载状态webviewStyles: {// width: '100%',// height: '90%'},};},onLoad(options) {// 获取页面参数this.title = options.title || "";this.pdfUrl = decodeURIComponent(options.url || "");// 验证PDF URLif (!this.pdfUrl) {uni.showToast({title: "缺少PDF文件地址",icon: "none",duration: 2000,});setTimeout(() => {this.handleBack();}, 2000);return;}// #ifdef H5window.addEventListener('message', (e) => {// console.log(e,e.data.data[0].action)this.dealMessage(e.data.data[0])})// #endif// 初始化web-view URLthis.initWebviewUrl();},methods: {/*** 初始化web-view加载的URL*/initWebviewUrl() {// 构建PDF预览页面的URL(使用hybrid中的html页面)// 注意:根据实际项目目录结构调整路径let viewerUrl = "/static/html/pdfViewer.html";// 处理不同平台的路径// #ifdef H5// 添加参数:PDF地址和标题const params = new URLSearchParams();params.append("url", this.pdfUrl);params.append("title", this.title);console.log(params);// H5平台需要完整路径viewerUrl = location.origin + viewerUrl + "?" + params.toString();// #endif// #ifndef H5// 非H5平台直接拼接参数// 添加参数:PDF地址和标题viewerUrl =viewerUrl + "?" + "url=" + this.pdfUrl + "&title=" + this.title;// #endifthis.webviewUrl = viewerUrl;},/*** 处理web-view发送的消息*/handleWebviewMessage(e) {console.log(e)this.dealMessage(e.detail.data[0][0])},dealMessage(data) {if (data.action === "back") {// 处理返回事件this.handleBack();} else if (data.action === "loaded") {// 处理PDF加载完成事件this.loading = false;} else if (data.action === "error") {// 处理PDF加载错误this.loading = false;uni.showToast({title: "PDF加载失败",icon: "none",duration: 2000,});}},/*** 处理返回按钮点击*/handleBack() {uni.navigateBack({delta: 1, // 返回的页面数,1表示返回上一级success: function () {console.log("返回上一页成功");},fail: function (error) {console.error("返回上一页失败", error);},});},},
};
</script><style lang="scss" scoped>
.container {display: flex;flex-direction: column;height: 100vh;
}.navbar {display: flex;align-items: center;height: 44px;background-color: #fff;border-bottom: 1px solid #eee;padding: 0 16px;/* #ifndef H5 */padding-top: 44px;height: 88px;/* #endif */box-sizing: border-box;
}.back-btn {width: 44px;height: 44px;display: flex;align-items: center;justify-content: center;
}.title {flex: 1;text-align: center;font-size: 17px;color: #333;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;
}.right-space {width: 44px;height: 44px;
}.webview {flex: 1;width: 100%;
}.loading-mask {position: fixed;top: 44px;left: 0;right: 0;bottom: 0;background-color: rgba(255, 255, 255, 0.8);display: flex;align-items: center;justify-content: center;z-index: 999;
}.loading-container {display: flex;flex-direction: column;align-items: center;
}.loading-text {margin-top: 12px;font-size: 14px;color: #666;
}
</style>

2、预览页面

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8" /><metaname="viewport"content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/><title>PDF预览</title><!-- 引入PDF.js库 --><script src="./pdf.min.js"></script><script type="text/javascript" src="./uni.webview.1.5.6.js"></script><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {background-color: #f5f5f5;overflow-x: hidden;}#pdfContainer {padding: 16px;margin-top: 88px;}.pdf-page {width: 100%;margin-bottom: 16px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);background-color: #fff;border-radius: 4px;}.loading {position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);text-align: center;}.loading-text {margin-top: 16px;font-size: 14px;color: #333;}.error-container {position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);text-align: center;padding: 20px;}.error-text {font-size: 16px;color: #f66;margin-bottom: 20px;}.retry-btn {padding: 8px 16px;background: linear-gradient(300deg, #02c6ea 0%, #67f2b1 100%);color: white;border: none;border-radius: 20px;font-size: 14px;cursor: pointer;}.navbar {position: fixed;top: 0;left: 0;width: 100%;z-index: 99999;display: flex;width: 100%;align-items: center;background-color: #fff;border-bottom: 1px solid #eee;/* #ifndef H5 */padding-top: 44px;height: 88px;/* #endif */box-sizing: border-box;}.navbar.h5 {height: 44px;padding: 0 16px;}.back-btn {width: 44px;height: 44px;display: flex;align-items: center;justify-content: center;}.back-icon {width: 9px;height: 15px;background-image: url(./back.png);background-repeat: no-repeat;background-position: 0 0;background-size: 100% 100%;}.title {flex: 1;text-align: center;font-size: 17px;color: #333;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}.title > div {padding-right: 22px;}.right-space {width: 44px;height: 44px;}</style></head><body><div class="navbar" id="nav"><div class="back-btn" id="back"><div class="back-icon"></div></div><div class="title"><div>PDF预览</div></div></div><div id="loading" class="loading"><div class="loading-text">加载中...</div></div><div id="error" class="error-container" style="display: none"><div class="error-text">PDF加载失败</div><button class="retry-btn" id="retryBtn">重试</button></div><div id="pdfContainer"></div><script>let env = null;document.addEventListener("UniAppJSBridgeReady", function () {uni.getEnv(function (res) {env = res;console.log("当前环境:" + JSON.stringify(res));if (res.h5) {const cls = document.getElementById("nav").className;document.getElementById("nav").setAttribute("class", cls + " h5");//  console.log(cls)}});});// 向uni-app发送消息function sendMessage(action, data = {}) {console.log(env);if (env.h5) {// H5环境window.parent.postMessage({data: [{ action, ...data }],},"*");} else {uni.postMessage({data: [{ action, ...data }],});}}// 获取URL参数function getUrlParams() {const params = {};const search = window.location.search.substring(1);const pairs = search.split("&");pairs.forEach((pair) => {const [key, value] = pair.split("=");if (key) {params[key] = decodeURIComponent(value || "");}});return params;}// 初始化PDF预览async function initPdfViewer() {const params = getUrlParams();let pdfUrl = params.url;if (!pdfUrl) {showError();return;}try {// 处理本地文件路径(App平台)if (pdfUrl.startsWith("_doc/") || pdfUrl.startsWith("_downloads/")) {pdfUrl = window.location.origin + pdfUrl;}console.log(pdfUrl);// 配置PDF.jsconst pdfjsLib = window["pdfjs-dist/build/pdf"];pdfjsLib.GlobalWorkerOptions.workerSrc = "./pdf.worker.min.js";// 加载PDF文档const loadingTask = pdfjsLib.getDocument(pdfUrl);const pdfDoc = await loadingTask.promise;console.log(pdfDoc);// 隐藏加载状态,通知uni-app加载完成document.getElementById("loading").style.display = "none";sendMessage("loaded");// 渲染所有页面for (let pageNum = 1; pageNum <= pdfDoc.numPages; pageNum++) {const page = await pdfDoc.getPage(pageNum);// 设置缩放比例以适应移动设备const viewport = page.getViewport({ scale: 1.2 });// 创建画布元素const canvas = document.createElement("canvas");canvas.className = "pdf-page";document.getElementById("pdfContainer").appendChild(canvas);const context = canvas.getContext("2d");canvas.height = viewport.height;canvas.width = viewport.width;// 渲染页面const renderContext = {canvasContext: context,viewport: viewport,};await page.render(renderContext).promise;}} catch (error) {console.error("PDF加载失败:", error);showError();sendMessage("error", { message: error.message });}}// 显示错误状态function showError() {document.getElementById("loading").style.display = "none";document.getElementById("error").style.display = "block";}// 重试按钮事件document.getElementById("retryBtn").addEventListener("click", () => {document.getElementById("error").style.display = "none";document.getElementById("loading").style.display = "block";document.getElementById("pdfContainer").innerHTML = "";initPdfViewer();});// 页面加载完成后初始化window.addEventListener("DOMContentLoaded", initPdfViewer);window.onload = () => {document.getElementById("back").addEventListener("click", () => {console.log("back");sendMessage("back");});};// 监听页面返回事件(Android物理返回键)document.addEventListener("backbutton",() => {sendMessage("back");},false);</script></body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/93662.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/93662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《用Proxy解构前端壁垒:跨框架状态共享库的从零到优之路》

一个项目中同时出现React的函数式组件、Vue的模板语法、Angular的依赖注入时,数据在不同框架体系间的流转便成了开发者不得不面对的难题—状态管理,这个本就复杂的命题,在跨框架场景下更显棘手。而Proxy,作为JavaScript语言赋予开发者的“元编程利器”,正为打破这道壁垒提…

MOESI FSM的全路径测试用例

MOESI FSM的全路径测试用例摘要&#xff1a;本文首先提供一个UVM版本的测试序列&#xff08;基于SystemVerilog和UVM框架&#xff09;&#xff0c;设计为覆盖MOESI FSM的全路径&#xff1b;其次详细解释如何使用覆盖组&#xff08;covergroup&#xff09;来量化测试的覆盖率&am…

git仓库和分支的关系

1️⃣ 仓库分支&#xff08;Repository Branch&#xff09;每个 Git 仓库都有自己的分支结构。分支决定你当前仓库看到的代码版本。示例&#xff1a;仓库分支只是局部修改&#xff0c;项目分支才是全局管理所有仓库分支的概念。wifi_camera 仓库&#xff1a; - main - dev - fe…

Linux的基本操作

Linux 系统基础操作完整指南一、文件与目录操作1. 导航与查看pwd (Print Working Directory)作用&#xff1a;显示当前所在目录的完整路径示例&#xff1a;pwd → 输出 /home/user/documents使用场景&#xff1a;当你在多层目录中迷失时快速定位当前位置ls (List)常用选项&…

npm设置了镜像 pnpm还需要设置镜像吗

npm配置镜像后是否需要为pnpm单独设置镜像&#xff1f; 是的&#xff0c;即使您已经为npm设置了镜像源&#xff08;如淘宝镜像&#xff09;&#xff0c;仍然需要单独为pnpm配置镜像源。这是因为npm和pnpm是两个独立的包管理工具&#xff0c;它们的配置系统和环境变量是分离的&a…

Linux管道

预备知识&#xff1a;进程通信进程需要某种协同&#xff0c;协同的前提条件是通信。有些数据是用来通知就绪的&#xff0c;有些是单纯的传输数据&#xff0c;还有一些是控制相关信息。进程具有独立性&#xff0c;所以通信的成本可能稍微高一点&#xff1b;进程间通信前提是让不…

基于Spring Boot的快递物流仓库管理系统 商品库存管理系统

&#x1f525;作者&#xff1a;it毕设实战小研&#x1f525; &#x1f496;简介&#xff1a;java、微信小程序、安卓&#xff1b;定制开发&#xff0c;远程调试 代码讲解&#xff0c;文档指导&#xff0c;ppt制作&#x1f496; 精彩专栏推荐订阅&#xff1a;在下方专栏&#x1…

脚手架开发-Common封装基础通用工具类<基础工具类>

书接上文 java一个脚手架搭建_redission java脚手架-CSDN博客 以微服务为基础搭建一套脚手架开始前的介绍-CSDN博客 脚手架开发-准备配置-进行数据初始化-配置文件的准备-CSDN博客 脚手架开发-准备配置-配置文件的准备项目的一些中间件-CSDN博客 脚手架开发-Nacos集成-CSD…

软件系统运维常见问题

系统部署常见问题 环境配置、兼容性问题。生产与测试环境的操作系统、库版本、中间件版本不一致&#xff0c;运行环境软件版本不匹配。新旧版本代码/依赖不兼容。依赖缺失或冲突问题。后端包启动失败&#xff0c;提示类/方法/第三方依赖库找不到或者版本冲突。配置错误。系统启…

2021 IEEE【论文精读】用GAN让音频隐写术骗过AI检测器 - 对抗深度学习的音频信息隐藏

使用GAN生成音频隐写术的隐写载体 本文为个人阅读GAN音频隐写论文&#xff0c;部分内容注解&#xff0c;由于原文篇幅较长这里就不再一一粘贴&#xff0c;仅对原文部分内容做注解&#xff0c;仅供参考详情参考原文链接 原文链接&#xff1a;https://ieeexplore.ieee.org/abstra…

PWA技术》》渐进式Web应用 Push API 和 WebSocket 、webworker 、serviceworker

PWA # 可离线 # 高性能 # 无需安装 # 原生体验Manifest {"name": "天气助手", // 应用全名"short_name": "天气", // 短名称&#xff08;主屏幕显示&#xff09;"start_url": "/index.html&…

数据结构——栈和队列oj练习

225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09; 这一题需要我们充分理解队列和栈的特点。 队列&#xff1a;队头出数据&#xff0c;队尾入数据。 栈&#xff1a;栈顶出数据和入数据。 我们可以用两个队列实现栈&#xff0c;在这过程中&#xff0c;我们总要保持其…

Java基础 8.19

目录 1.局部内部类的使用 总结 1.局部内部类的使用 说明&#xff1a;局部内部类是定义在外部类的局部位置&#xff0c;比如方法中&#xff0c;并且有类名可以直接访问外部类的所有成员&#xff0c;包含私有的不能添加访问修饰符&#xff0c;因为它的地位就是一个局部变量。局…

从父类到子类:C++ 继承的奇妙旅程(2)

前言&#xff1a;各位代码航海家&#xff0c;欢迎回到C继承宇宙&#xff01;上回我们解锁了继承的「基础装备包」&#xff0c;成功驯服了public、protected和花式成员隐藏术。但——⚠️前方高能预警&#xff1a; 继承世界的暗流涌动远不止于此&#xff01;今天我们将勇闯三大神…

【图像算法 - 16】庖丁解牛:基于YOLO12与OpenCV的车辆部件级实例分割实战(附完整代码)

庖丁解牛&#xff1a;基于YOLO12与OpenCV的车辆部件级实例分割实战&#xff08;附完整代码&#xff09; 摘要&#xff1a; 告别“只见整车不见细节”&#xff01;本文将带您深入实战&#xff0c;利用YOLO12-seg训练实例分割模型&#xff0c;结合OpenCV的强大图像处理能力&…

ubuntu22.04配置远程桌面

文章目录前言检查桌面类型xorg远程桌面(xrdp)安装xrdpxrdp添加到ssl-certwayland远程桌面(gnome-remote-desktop)检查安装开启开启状况检查自动登录奇技淫巧前言 在windows上使用远程桌面服务&#xff0c;连接ubuntu主机的远程桌面 检查桌面类型 查看桌面类型、协议 echo $…

SQL Server 中子查询、临时表与 CTE 的选择与对比

在 SQL Server 的实际开发过程中&#xff0c;我们常常需要将复杂的查询逻辑分解为多个阶段进行处理。实现这一目标的常见手段有 子查询 (Subquery)、临时表 (Temporary Table) 和 CTE (Common Table Expression)。这三者在语法、执行效率以及可维护性方面各有优势与局限。如何选…

肖臻《区块链技术与应用》第20-22讲 - 以太坊难度调整、权益证明和智能合约

以太坊的“冰河时代”:详解难度调整算法与“难度炸弹” 摘要: 为了实现远快于比特币的十几秒出块速度,以太坊必须设计一套更为灵敏和复杂的挖矿难度调整算法。本文基于北京大学肖臻老师的公开课内容,深入剖析了以太坊独特的逐块难度调整机制。文章首先解释了其维持15秒平均…

C++中内存池(Memory Pool)详解和完整示例

1. 什么是内存池&#xff1f; 内存池&#xff08;Memory Pool / Pool Allocator&#xff09; 是一种内存管理机制&#xff0c;提前向系统申请一大块内存&#xff0c;再在这块内存里切分、分配和回收。 它相当于在用户空间建立了一层 “小型堆管理器”&#xff0c;避免频繁调用系…

测试 Next.js 应用:工具与策略

1. 引言 Next.js 作为一个基于 React 的全栈框架&#xff0c;在构建复杂 Web 应用时&#xff0c;测试是确保代码质量、功能稳定性和用户体验的关键步骤。测试可以分为单元测试、集成测试和端到端测试三种类型&#xff0c;每种类型针对不同的层面&#xff1a;单元测试验证单个组…