uniapp 在App端根据页面内容生成PDF并保存在指定文件夹下打开(安卓)

866 阅读2分钟

首先安装插件html2canvas,JsPDF。 在renderjs中使用这两个插件,因为在app端无法直接使用。具体代码如下

<script module="render2" lang="renderjs">
	import html2Canvas from 'html2canvas'
  import JsPDF from 'jspdf'
	export default {
		data() {
			return {}
		},
		mounted() {},
		methods: {
			myprint(data) {
				this.$ownerInstance.callMethod('showLoading');
				const detail = document.querySelector("#content2PDF") //获取到html包含此页面的外层标签,detail为页面中需要导出为pdf的最外层标签的id名
				const imgHeight = detail.clientHeight;
				const imgWidth = detail.clientWidth;
				html2Canvas(detail, { //对应的dom元素id(class也可以)
					allowTaint: true, //是否允许跨域图像渲染画布
					useCORS: true, //是否尝试使用 CORS 从服务器加载图像 解决图片跨域问题
				}).then((canvas) => {
					return new Promise((resolve, reject) => {
						console.log(4);
						setTimeout(() => {
							console.log(5);
							resolve(canvas)
						}, 500)
					}).then((canvas) => {
						//生成的canvas实例
						var contentWidth = canvas.width; //所选元素宽度
						var contentHeight = canvas.height; //所选元素高度
						//一页pdf显示html页面生成的canvas高度;
						var pageHeight = contentWidth / 595.28 * 841.89;
						//未生成pdf的html页面高度
						var leftHeight = contentHeight;
						//pdf页面偏移
						var position = 0;
						//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
						var imgWidth = 555.28;
						var imgHeight = 555.28 / contentWidth * contentHeight;
						var pageData = canvas.toDataURL('image/jpeg', 1.0); //转成jpg格式
						var pdf = new JsPDF('', 'pt', 'a4'); //生成pdf实例
						//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
						//当内容未超过pdf一页显示的范围,无需分页
						if (leftHeight < pageHeight) {
							pdf.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight);
						} else {
							while (leftHeight > 0) {
								pdf.addImage(pageData, 'JPEG', 20, position, imgWidth, imgHeight)
								leftHeight -= pageHeight;
								position -= 841.89;
								//避免添加空白页
								if (leftHeight > 0) {
									pdf.addPage();
								}
							}
						}

						var blob = pdf.output("datauristring");
						console.log(7);
						// 在renderjs中无法使用uni和plus对象,这里调用另一个script中的方法
						this.$ownerInstance.callMethod('downPdf', blob);
					}).catch((r) => {
						console.log(r);
						this.$ownerInstance.callMethod('hideLoading');
					})
				});

			}
		},
	}
</script>

在页面中绑定下载事件

<button class="downloadBtn" type="primary" @click="render2.myprint">下载</button>

新建文件夹并将pdf保存进去,有两种文件写入方式,第一种base64过长生成pdf会报错,第二种可以避免这种问题

<script>
	export default {
		methods: {
			/**
			 * base64字符串转成文件
			 * @param {String} base64Str // 允许包含前缀
			 * @param {String} fileName // 文件名称:1663061363470.xlsx
			 * @param {Object} callback  // 返回本地路径径URL,file:///xxx/doc/1663062980631.xlsx
			 */
			base64ToFile(base64Str, fileName, callback) {
                          //申请本地存储读写权限,创建文件夹
				plus.android.requestPermissions([
					'android.permission.WRITE_EXTERNAL_STORAGE',
					'android.permission.READ_EXTERNAL_STORAGE',
					'android.permission.INTERNET',
					'android.permission.ACCESS_WIFI_STATE'
				], error => {

					const File = plus.android.importClass('java.io.File');
					let file = new File('/storage/emulated/0/文件夹名字');
					if (!file.exists()) { //文件夹不存在即创建
						return file.mkdirs();
					}
					return false;
				}, success => {
					uni.showToast({
						title: '无法获取权限,文件下载将出错!',
						icon: 'none',
					})
				})
                          // 去除base64前缀,进行文件保存
				var index = base64Str.indexOf(',')
				var base64Str = base64Str.slice(index + 1, base64Str.length)
				let that = this
				plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
					fs.root.getFile(fileName, {
						create: true
					}, function(entry) {
						// 获得本地路径URL,file:///xxx/doc/1663062980631.xlsx
						var fullPath = '/storage/emulated/0/文件夹名字/'+fileName;
						var Base64 = plus.android.importClass("android.util.Base64");
						var FileOutputStream = plus.android.importClass("java.io.FileOutputStream");
						var out = new FileOutputStream(fullPath);
						// 此处Base64.decode有长度限制,如果不能满足需求,可以考虑换成官方原生插件市场的【Base64转文件】
						// var bytes = Base64.decode(base64Str, Base64.DEFAULT);
                                          //转成Array避免长度限制
						let bytes = that.base64ToByteArray(base64Str);
						out.write(bytes);
						out.close();
						// 回调  
						callback && callback();
					})
				})
			}
                        
			downPdf(path) {
				var fileName = (new Date()).valueOf() + '.pdf';
				let that = this
				this.base64ToFile(path, fileName, function(path1) {
					that.hideLoading()
					uni.showToast({
						title: '已保存在文件夹下!',
						icon: 'none',
						duration: 2000,
						position :'top'
					});
					setTimeout(() => {
                                                //自行控制是否打开文件
						// plus.runtime.openFile(文件地址); //用第三方程序打开文件
					}, 2000)
				})
			},
                  base64ToByteArray(base64Str) {
				const binaryString = atob(base64Str);
				const uint8Array = new Uint8Array(binaryString.length);

				for (let i = 0; i < binaryString.length; i++) {
					uint8Array[i] = binaryString.charCodeAt(i);
				}
				let arr = []
				Array.from(uint8Array).map(num => {
					arr.push(num >= 128 ? (num - 256) : num)
				})
				return arr;
			},
			showLoading() {
				uni.showLoading({
					title: '导出到手机相册中'
				})
			},
			hideLoading() {
				uni.hideLoading()
			},
		}
	}
</script>

之后就可以在文件夹下看到pdf