uniapp + 安卓APP + H5 + 微信小程序实现PDF文件的预览和下载

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 = this
    this.$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 H5
        this.download4H5(fileUrl, attachmentName)
        // #endif

        //条件编译,若为App端,则需要将本地文件系统URL转换为平台绝对路径
        // #ifdef APP-PLUS
        this.download4App(fileUrl, attachmentName)
        // #endif

        // #ifdef MP-WEIXIN
        this.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官方的下载api
                            uni.downloadFile({
                                //需要预览的文件地址
                                url: fileUrl,
                                header: {
                                    "Access-Control-Expose-Headers": 'Content-Disposition'
                                },
                                success: (res) => {
                                    if (res.statusCode === 200) {
                                        //下载成功,得到文件临时地址
                                        console.log('下载成功', res.tempFilePath);
                                        uni.hideLoading();

                                        //条件编译,若为h5端则直接赋值文件地址
                                        // #ifdef H5
                                        let newUrl = res.tempFilePath
                                        // uni.hideLoading();
                                        //这里新建一个vue页面,跳转并预览pdf文档
                                        uni.navigateTo({
                                            url: "/pages/pdfView?url=" + newUrl,
                                        })
                                        // #endif

                                        //条件编译,若为App端,则需要将本地文件系统URL转换为平台绝对路径	
                                        // #ifdef APP-PLUS
                                        let newUrl = plus.io.convertLocalFileSystemURL(res.tempFilePath)
                                        // uni.hideLoading();
                                        //这里新建一个vue页面,跳转并预览pdf文档
                                        uni.navigateTo({
                                            url: "/pages/pdfView?url=" + newUrl,
                                        })
                                        // #endif

                                        // #ifdef MP-WEIXIN
                                        let newUrl = res.tempFilePath
                                        // 微信小程序中使用 wx.openDocument
                                        wx.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"

5、后续补充代码(下载进度,loading封装)

1、loading的封装(手机APP中使用uni的loading会导致闪烁,所以分开写)


data() {
	return {
		loading : null
	}	
},

methods: {
	closeLoading(){
		this.loading = null
		// #ifdef APP-PLUS
		plus.nativeUI.closeWaiting();
		// #endif
		
		// #ifdef H5 || MP-WEIXIN
		uni.hideLoading();
		// #endif
	},
	
	showLoading(title){
		// #ifdef H5 || MP-WEIXIN
		uni.showLoading({
			title: title,
			mask: true
		});
		// #endif
		
		// #ifdef APP-PLUS
		if(this.loading){
			this.loading.setTitle(title)
		}else{
			this.loading = plus.nativeUI.showWaiting(title);
		}
		// #endif
	},
	
	showToast(title,duration){
		if(!duration){
			duration = 1500
		}
		uni.showToast({
		    title: title,
			icon: 'none',
		    duration: duration
		});
	},
}

2、下载进度

1、H5 || 微信小程序

uni.downloadFile 增加一个返回参数,以下代码:
const downloadTask = uni.downloadFile(......)

downloadTask.onProgressUpdate((res) => {
	this.showLoading('已下载 ' + res.progress + ' %')
});

2、安卓APP

let task = plus.downloader.createDownload(..............)
task.start();
let pre_percent = 0
task.addEventListener('statechanged', function (task, status) {
    switch (task.state) {
      case 1:  // 开始
        console.log('开始下载');
        break;
      case 2:  // 已连接到服务器
        console.log('已连接到服务器');
        break;
      case 3:  // 正在下载
		  let percent = parseInt(parseFloat(task.downloadedSize) / parseFloat(task.totalSize) * 100)
		  if (percent > pre_percent) {
			  _this.showLoading('已下载 ' + percent + ' %' + " ")
			  pre_percent = percent
		  }
        console.log(`已下载: ${task.downloadedSize} 总大小: ${task.totalSize}`);
        break;
      case 4:  // 下载完成
        console.log('下载完成');
        break;
    }
  });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值