uniapp + 安卓APP + H5 + 微信小程序实现PDF文件的预览和下载
文章目录
- uniapp + 安卓APP + H5 + 微信小程序实现PDF文件的预览和下载
- 1、用到的技术及插件
- 2、简述操作:
- 下载
- 预览
- 3、上代码:(主要是写后端,前端不大熟,我感觉写的还凑活,不对的请指正嘻嘻)
- 4、注意的问题
uniapp + 安卓APP + H5 + 微信小程序实现PDF文件的预览和下载
1、用到的技术及插件
uniapp、pdf.js(4.0.379,我的node是v16.15.0,这个版本正好)、微信小程序api
2、简述操作:
下载
安卓APP:点击“下载”后获取手机授权,可下载到手机“文件app”的最外层可访问目录下“file://storage/emulated/0/com.custom/”H5:安卓:点击“下载”后,基本上所有的浏览器均可弹窗下载苹果:(推荐使用Safari浏览器,其他浏览器每个效果都不一样)点击“下载”后预览PDF文件,点击页面中间的“分享”按钮,选择“保存到文件”微信小程序:安卓:点击“下载”后预览PDF文件,点击右上角“...”三个点,选择“保存到手机”苹果:点击“下载”后预览PDF文件,点击右上角“...”三个点,选择用“其他应用打开”,再点击“保存到文件”
预览
安卓APP:点击“预览”后通过pdf.js实现预览H5:点击“预览”后通过pdf.js实现预览微信小程序:点击“预览”后通过小程序内实现预览
3、上代码:(主要是写后端,前端不大熟,我感觉写的还凑活,不对的请指正嘻嘻)
提示:主要是通过一个接口获取服务器端文件的路径
1、在项目static目录下新建一个pdf目录,将pdf.js解压后两个目录个一个文件复制到该目录下
2、新建一个预览页面
<template><view><web-view :src = "url"></web-view></view>
</template><script>export default {data() {return {url : '',viewerUrl:'/static/pdf/web/viewer.html?file=',//刚解压的文件地址,用来渲染PDF的html} },onLoad(options) {this.url = this.viewerUrl + options.url//将插件地址与接收的文件地址拼接起来},}
</script>
以下为业务代码
// APP下载------------------------------------START----------------------------------------------------
handlePdfDownload(approvalId, attachmentName) {uni.showLoading({title: '下载中...'});let data = {approvalId: approvalId,downloadType: '1'}let _this = thisthis.$api.requestPost('/api', data, true).then(res => {if (res.code && res.code != 200) {uni.hideLoading();uni.showToast({title: res.msg,icon: 'none',duration: 3000});return;}let fileUrl = `${url_config}filePath${res.data}`;//条件编译,若为h5端则直接赋值文件地址// #ifdef H5this.download4H5(fileUrl, attachmentName)// #endif//条件编译,若为App端,则需要将本地文件系统URL转换为平台绝对路径// #ifdef APP-PLUSthis.download4App(fileUrl, attachmentName)// #endif// #ifdef MP-WEIXINthis.download4WX(fileUrl, attachmentName)// #endif})
},async createDir(path) {try {// 申请本地存储读写权限return new Promise((resolve, reject) => {plus.android.requestPermissions(['android.permission.WRITE_EXTERNAL_STORAGE','android.permission.READ_EXTERNAL_STORAGE','android.permission.INTERNET','android.permission.ACCESS_WIFI_STATE'], function (e) {if (e.deniedAlways.length > 0) { //权限被永久拒绝reject(new Error('权限被永久拒绝'));}if (e.deniedPresent.length > 0) {//权限被临时拒绝reject(new Error('权限被临时拒绝'));}if (e.granted.length > 0) { //权限被允许//调用依赖获取读写手机储存权限的代码// _this.exportFile()const File = plus.android.importClass('java.io.File');let file = new File(path);if (!file.exists()) { // 文件夹不存在即创建if (file.mkdirs()) {resolve(true); // 成功创建文件夹} else {reject(new Error('未能创建文件夹'));//resolve(false); // 未能创建文件夹}} else {resolve(true); // 文件夹已存在}}}, function (e) {});});} catch (error) {console.error('权限请求失败:', error);return false; // 返回 false 表示权限请求失败}},async download4App(fileUrl, attachmentName) {try {// 获取文件夹路径const path = '/storage/emulated/0/com.custom';// 确保文件夹存在const createDirResult = await this.createDir(path);// 检查文件夹是否创建成功if (createDirResult) {// 处理文件名let lastPointIndex = attachmentName.lastIndexOf('.');let fileType = attachmentName.substring(lastPointIndex + 1);let endFileName = attachmentName.substring(0, lastPointIndex) + '_' + new Date().getTime() + '.pdf';// 开始文件下载let task = plus.downloader.createDownload(fileUrl, {filename: 'file://storage/emulated/0/com.custom/' + endFileName}, function (d, status) {if (status === 200) {uni.hideLoading();uni.showToast({icon: 'none',title: '成功下载到【手机文件->"com.custom"目录->' + endFileName + '】',duration: 3000});// d.filename是文件在保存在本地的相对路径,使用下面的API可转为平台绝对路径let fileSaveUrl = plus.io.convertLocalFileSystemURL(d.filename);console.log(fileSaveUrl);// plus.runtime.openFile(d.filename)//选择软件打开文件} else {uni.hideLoading();uni.showToast({icon: 'none',title: '下载失败',});plus.downloader.clear();}});uni.showLoading({title: '下载中...'});task.start();} else {uni.hideLoading();uni.showToast({title: '权限请求失败',icon: 'none',});console.error('权限请求失败');}} catch (error) {uni.hideLoading();uni.showToast({title: error.message,icon: 'none',});console.error('下载过程中发生错误:', error);}},download4H5(fileUrl, attachmentName) {uni.downloadFile({//需要预览的文件地址url: fileUrl,header: {"Access-Control-Expose-Headers": 'Content-Disposition'},success: (res) => {if (res.statusCode === 200) {uni.hideLoading();//下载成功,得到文件临时地址console.log('下载成功', res.tempFilePath);let newUrl = res.tempFilePath// 创建一个临时的 <a> 元素用于下载const link = document.createElement('a');link.href = newUrl;link.setAttribute('download', attachmentName);document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(link.href);} else {uni.hideLoading();uni.showToast({title: '下载失败',icon: 'none',})}},fail() {uni.hideLoading();uni.showToast({title: '下载异常',icon: 'none',})}});},download4WX(fileUrl, attachmentName) {wx.downloadFile({url: fileUrl,success(res) {// 只要服务器有响应数据,就会把响应内容写入文件并进入 success 回调,业务需要自行判断是否下载到了想要的内容if (res.statusCode === 200) {uni.hideLoading();wx.openDocument({filePath: res.tempFilePath,showMenu: true, //关键点success: function (res) {console.log('打开文档成功')},fail: function (err) {uni.showToast({title: '文档打开失败',icon: 'none',duration: 3000});}})} else {uni.hideLoading();uni.showToast({title: '文档下载失败',icon: 'none',});}},fail() {uni.hideLoading();uni.showToast({title: '下载异常',icon: 'none',})}})},// APP下载------------------------------------END----------------------------------------------------// APP预览------------------------------------START----------------------------------------------------handlePdfOpen(approvalId) {uni.showLoading({title: '正在打开文档...'});let data = {approvalId: approvalId,downloadType: '0'}this.$api.requestPost('/api', data, true).then(res => {// uni.hideLoading();if (res.code && res.code != 200) {uni.hideLoading();uni.showToast({title: res.msg,icon: 'none',duration: 3000});return;}this.previewPdf(`${url_config}filePath${res.data}`)})},previewPdf(fileUrl) {//uniapp官方的下载apiuni.downloadFile({//需要预览的文件地址url: fileUrl,header: {"Access-Control-Expose-Headers": 'Content-Disposition'},success: (res) => {if (res.statusCode === 200) {//下载成功,得到文件临时地址console.log('下载成功', res.tempFilePath);uni.hideLoading();//条件编译,若为h5端则直接赋值文件地址// #ifdef H5let newUrl = res.tempFilePath// uni.hideLoading();//这里新建一个vue页面,跳转并预览pdf文档uni.navigateTo({url: "/pages/pdfView?url=" + newUrl,})// #endif//条件编译,若为App端,则需要将本地文件系统URL转换为平台绝对路径 // #ifdef APP-PLUSlet newUrl = plus.io.convertLocalFileSystemURL(res.tempFilePath)// uni.hideLoading();//这里新建一个vue页面,跳转并预览pdf文档uni.navigateTo({url: "/pages/pdfView?url=" + newUrl,})// #endif// #ifdef MP-WEIXINlet newUrl = res.tempFilePath// 微信小程序中使用 wx.openDocumentwx.openDocument({filePath: newUrl, // 文件路径fileType: 'pdf', // 文件类型success: function (res) {console.log('文档打开成功');},fail: function (err) {uni.showToast({title: '文档打开失败',icon: 'none',duration: 3000});}});// #endif } else {uni.hideLoading();uni.showToast({title: '下载失败',icon: 'none',})}},fail() {uni.hideLoading();uni.showToast({title: '下载异常',icon: 'none',})}});},// APP预览------------------------------------END----------------------------------------------------
4、注意的问题
1、H5预览出现“Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of”
nginx中加入如下配置
include mime.types;
types
{application/javascript mjs;
}
2、pdf.js出现跨域问题
可以将 view.js 的以下代码注释掉
if (origin !== viewerOrigin && protocol !== 'blob:') { throw new Error('file origin does not match viewer\'s'); }
3、pdf.js隐藏不需要的按钮
可以加上如下样式
style="visibility:hidden"
相关文章:
uniapp + 安卓APP + H5 + 微信小程序实现PDF文件的预览和下载
文章目录 uniapp 安卓APP H5 微信小程序实现PDF文件的预览和下载1、用到的技术及插件2、简述操作:下载预览 3、上代码:(主要是写后端,前端不大熟,我感觉写的还凑活,不对的请指正嘻嘻)4、注意的问题 uniapp 安卓APP…...
Elasticsearch 8 RAG 技术分享
作者:来自 Elastic 中国区首席架构师 Jerry 本文由 Elastic 中国区首席架构师 Jerry Zhu 在【AI 搜索 TechDay】上的分享整理而成。【AI 搜索 TechDay】 是 Elastic 和阿里云联合主办的 AI 技术 Meetup 系列,聚焦企业级 AI 搜索应用和开发者动手实践&am…...
根据字典值回显,有颜色的
背景 本项目以若依前端vue2版本为例,项目中有根据字典值回显文本的函数selectDictLabel,但是有时候我们需要带颜色的回显,大概这样的 用法 <template v-slotscope><dict-label :options"dangerLevelOptions" :value&qu…...
多台PC网络ADB连接同一台RK3399 Android7.1.2设备
在RK3399 Android7.1.2上面,进行网络ADB调试时,如果多台电脑连接同一台Android设备,第一台连接上的能正常操作,之后连接的看到设备状态为OFFLINE,分析了下ADBD相关代码,发现在ACCEPT Client的时候没有区分别…...
前端黑科技:使用 JavaScript 实现网页扫码功能
在数字化时代,二维码已经渗透到我们生活的方方面面。从移动支付到产品溯源,二维码凭借其便捷性和高效性,成为了信息传递的重要载体。而随着前端技术的不断发展,我们甚至可以使用 JavaScript 在网页端实现二维码扫描功能࿰…...
【人工智能】全景解析:【机器学习】【深度学习】从基础理论到应用前景的【深度探索】
目录 1. 人工智能的基本概念 1.1 人工智能的定义与发展 1.1.1 人工智能的定义 1.1.2 人工智能的发展历史 1.2 人工智能的分类 1.2.1 弱人工智能 1.2.2 强人工智能 1.2.3 超人工智能 1.3 人工智能的关键组成部分 1.3.1 数据 1.3.2 算法 1.3.3 计算能力 2. 机器学习…...
MySQL与PostgreSQL语法区别
1. 数据类型差异 a. 整型 ● MySQL中的text数据类型最大存储容量为64KB,PostgreSQL中的text类型没有此限制。 ● MySQL中使用tinyint、mediumint和int表示不同大小的整数,PostgreSQL使用smallint、int和bigint。 b. 浮点数类型 ● MySQL提供了float和…...
vue2+OpenLayers 天地图上凸显出当前地理位置区域(4)
凸显出当前区域 需要当前地方的json数据 这个可以在阿里的这个阿里 看下效果图 遮盖层的逃命都是可以调的 引入 下面一段代码 import sx from "/views/json/sx1.json"; // 下载的json import GeoJSON from "ol/format/GeoJSON"; // ol的一些方法 imp…...
基于Python、Django开发Web计算器
1、创建项目 创建Django项目参照https://blog.csdn.net/qq_42148307/article/details/140798249,其中项目名为compute,并在该项目下创建一个名为app的应用,并且进行基本的配置。 2、导入Bootstrap前端框架 Bootstrap的使用参照https://blo…...
高性能并行计算面试-核心概念-问题理解
目录 1.什么是并行计算?高性能从哪些方面体现? 2.CPU常见的并行技术 3.GPU并行 4.并发与并行 5.常见的并行计算模型 6.如何评估并行程序的性能? 7.描述Am达尔定律和Gustafson定律,并解释它们对并行计算性能的影响 8.并行计…...
java-activiti笔记
版本:activiti7 <dependency><groupId>org.activiti</groupId><artifactId>activiti-json-converter</artifactId><version>7.0.0.Beta2</version><exclusions><exclusion><groupId>org.mybatis</g…...
Layui——隐藏表单项后不再进行验证
目录 修改后的部分代码 修改后的完整代码 我编辑用户信息和添加新用户用的是同一个表单,不同的是编辑用户信息里没有密码项和确认密码项,但是把它们隐藏后仍然要进行验证,也就是说它们俩的验证并没有随着表单项的隐藏而关闭。原因…...
Github Copilot 使用技巧
🎯目标读者 本文不包含如何安装 Github Copilot本文介绍了 Github Copilot 使用方法和一些技巧 本人已经使用 Github Copilot 2 年了,交了 3 次年费,每年 100$ 着实心痛,但是用着确实爽歪歪 但是感觉一直只用了一小部分功能&am…...
【实现100个unity特效之20】用unity实现物品悬浮和发光像素粒子特效
最终效果 文章目录 最终效果新增飞升粒子效果光圈效果修改不同颜色完结 新增飞升粒子效果 效果 光圈效果 效果 修改不同颜色 完结 赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持…...
GPT-4o mini发布,轻量级大模型如何颠覆AI的未来?
从巨无霸到小巨人:GPT-4o Mini的创新之路 ©作者|潇潇 来源|神州问学 引言 随着人工智能技术的飞速进步,AI领域的竞争日益激烈,大型模型的发布几乎成为常态。然而,这些庞大的模型通常需要大量的计算资源和存储空间ÿ…...
高性能的 C++ Web 开发框架 CPPCMS + WebSocket 模拟实现聊天与文件传输案例。
1. 项目结构 2. config.json {"service": {"api": "http","port": 8080,"ip": "0.0.0.0"},"http": {"script": "","static": "/static"} }3. CMakeLists.txt…...
合合信息OCR支持30类国内常见票据一站式分类识别,支持医疗发票、数电票识别
合合信息TextIn平台明星产品——国内通用票据识别,重磅更新! 产品支持票据类型扩展到23大类、30小类,覆盖场景更全面,同时升级优化了多款票据识别模型,平均识别率较前版本提升11.5%,整体识别速度提升21.9%…...
LeetCode-day40-3151. 特殊数组 I
LeetCode-day40-3151. 特殊数组 I 题目描述示例示例1:示例2:示例3: 思路代码 题目描述 如果数组的每一对相邻元素都是两个奇偶性不同的数字,则该数组被认为是一个 特殊数组 。 Aging 有一个整数数组 nums。如果 nums 是一个 特殊…...
技术研究:Redis 数据结构与 I/O 模型
数据结构 Redis之所以“快”,一方面因为它是内存数据库,所有操作都在内存上完成,内存的访问速度本来就快。另一方面则是因为高效的数据结构,使得操作键值效率较高。总体来说,Redis使用了一个用来保存每个Key/Value的全…...
46-扇孔的处理及铺铜以及布线
1.先连信号线 2.电源管脚,以如下方式处理: 引线打孔处理...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...
图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...
