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.电源管脚,以如下方式处理: 引线打孔处理...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...

ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

rm视觉学习1-自瞄部分
首先先感谢中南大学的开源,提供了很全面的思路,减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接:https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架: 代码框架结构:readme有…...