手机端 Android WebView 获取 blob 链接文件名并下载网页动态生成的 pdf 文件且调用外部程序打开 您所在的位置:网站首页 手机app怎么生成链接的文件 手机端 Android WebView 获取 blob 链接文件名并下载网页动态生成的 pdf 文件且调用外部程序打开

手机端 Android WebView 获取 blob 链接文件名并下载网页动态生成的 pdf 文件且调用外部程序打开

2024-07-01 12:51| 来源: 网络整理| 查看: 265

目录

1、已有可实用的动态 pdf 生成方案,

2、安卓编辑器中建立 Layout ,添加 WebView :

3、extends Activity 将上面的 WebView 赋值给变量(实例化?),并设置其大堆属性,以扩展其功能:

4、引入参考资料 1 的方法,定义下载完成监听器

5、获取 Blob 链接文件名及类型

6、重写 WebView 的下载监听器 DownloadListener 

7、其中的几个变量及其他说明

参考资料:

1.Android WebView支持下载blob协议文件_Misdirection_XG的博客-CSDN博客_android blob

2. Android Webview实现文件下载功能 - huidaoli - 博客园

3.base64和Blob互相转换_weixin_30776863的博客-CSDN博客

4.Android:你要的WebView与JS交互方式都在这里了 - 百度文库

应用于:html转pdf文件下载之最合理的方法支持中文_jessezappy的博客-CSDN博客

项目需求:

1.网页动态生成 pdf 文件。

2.手机打开以上网页下载 pdf 文件,并用外部程序打开 pdf 文件。

之前的文章:html转pdf文件下载之最合理的方法支持中文_jessezappy的博客-CSDN博客 ,中已经实现了网页动态生成 pdf 文件,通过好多种方式都可在 PC 端实现自动下载动态生成的 pdf 文件,如:

pdf.save("A4.pdf") ;

但我项目原计划是用手机打开这个网页下载 pdf 的,那些方法到了手机端,用 WebView 打开网页后,均无法下载由网页 js 生成的 pdf 文档。

深度研究 jspdf.umd.js 后,发现,其输出 pdf 数据有好多种参数:

/** * Generates the PDF document. * * If `type` argument is undefined, output is raw body of resulting PDF returned as a string. * * @param {string} type A string identifying one of the possible output types. * Possible values are: * 'arraybuffer' -> (ArrayBuffer) * 'blob' -> (Blob) * 'bloburi'/'bloburl' -> (string) * 'datauristring'/'dataurlstring' -> (string) * 'datauri'/'dataurl' -> (undefined) -> change location to generated datauristring/dataurlstring * 'dataurlnewwindow' -> (window | null | undefined) throws error if global isn't a window object(node) * 'pdfobjectnewwindow' -> (window | null) throws error if global isn't a window object(node) * 'pdfjsnewwindow' -> (wind | null) * @param {Object|string} options An object providing some additional signalling to PDF generator. * Possible options are 'filename'. * A string can be passed instead of {filename:string} and defaults to 'generated.pdf' * @function * @instance * @returns {string|window|ArrayBuffer|Blob|jsPDF|null|undefined} * @memberof jsPDF# * @name output */ var output = API.output = API.__private__.output = SAFE(function output(type, options) { options = options || {}; if (typeof options === "string") { options = { filename: options }; } else { options.filename = options.filename || "generated.pdf"; } switch (type) { case undefined: return buildDocument(); case "save": API.save(options.filename); break; case "arraybuffer": return getArrayBuffer(buildDocument()); case "blob": return getBlob(buildDocument()); case "bloburi": case "bloburl": // Developer is responsible of calling revokeObjectURL if (typeof globalObject.URL !== "undefined" && typeof globalObject.URL.createObjectURL === "function") { return globalObject.URL && globalObject.URL.createObjectURL(getBlob(buildDocument())) || void 0; } else { console.warn("bloburl is not supported by your system, because URL.createObjectURL is not supported by your browser."); } break; case "datauristring": case "dataurlstring": var dataURI = ""; var pdfDocument = buildDocument(); try { dataURI = btoa(pdfDocument); } catch (e) { dataURI = btoa(unescape(encodeURIComponent(pdfDocument))); } return "data:application/pdf;filename=" + options.filename + ";base64," + dataURI; case "pdfobjectnewwindow": if (Object.prototype.toString.call(globalObject) === "[object Window]") { var pdfObjectUrl = "https://cdnjs.cloudflare.com/ajax/libs/pdfobject/2.1.1/pdfobject.min.js"; var integrity = ' integrity="sha512-4ze/a9/4jqu+tX9dfOqJYSvyYd5M6qum/3HpCLr+/Jqf0whc37VUbkpNGHR7/8pSnCFw47T1fmIpwBV7UySh3g==" crossorigin="anonymous"'; if (options.pdfObjectUrl) { pdfObjectUrl = options.pdfObjectUrl; integrity = ""; } var htmlForNewWindow = "" + 'html, body { padding: 0; margin: 0; } iframe { width: 100%; height: 100%; border: 0;} PDFObject.embed("' + this.output("dataurlstring") + '", ' + JSON.stringify(options) + ");"; var nW = globalObject.open(); if (nW !== null) { nW.document.write(htmlForNewWindow); } return nW; } else { throw new Error("The option pdfobjectnewwindow just works in a browser-environment."); } case "pdfjsnewwindow": if (Object.prototype.toString.call(globalObject) === "[object Window]") { var pdfJsUrl = options.pdfJsUrl || "./examples/PDF.js/web/viewer.html"; var htmlForPDFjsNewWindow = "" + "html, body { padding: 0; margin: 0; } iframe { width: 100%; height: 100%; border: 0;} " + '' + ""; var PDFjsNewWindow = globalObject.open(); console.log(htmlForPDFjsNewWindow); if (PDFjsNewWindow !== null) { PDFjsNewWindow.document.write(htmlForPDFjsNewWindow); var scope = this; PDFjsNewWindow.document.documentElement.querySelector("#pdfViewer").onload = function () { PDFjsNewWindow.document.title = options.filename; PDFjsNewWindow.document.documentElement.querySelector("#pdfViewer").contentWindow.PDFViewerApplication.open(scope.output("bloburl")); }; } return PDFjsNewWindow; } else { throw new Error("The option pdfjsnewwindow just works in a browser-environment."); } case "dataurlnewwindow": if (Object.prototype.toString.call(globalObject) === "[object Window]") { var htmlForDataURLNewWindow = "" + "html, body { padding: 0; margin: 0; } iframe { width: 100%; height: 100%; border: 0;} " + "" + '' + ""; var dataURLNewWindow = globalObject.open(); if (dataURLNewWindow !== null) { dataURLNewWindow.document.write(htmlForDataURLNewWindow); dataURLNewWindow.document.title = options.filename; } if (dataURLNewWindow || typeof safari === "undefined") return dataURLNewWindow; } else { throw new Error("The option dataurlnewwindow just works in a browser-environment."); } break; case "datauri": case "dataurl": return globalObject.document.location.href = this.output("datauristring", options); default: return null; } });

以上这些参数,在 CallBack 中调用 pdf.Output 时使用:

var link = document.getElementById('linklink'); link.target = '_blank'; //link.href = window.URL.createObjectURL(convertBase64UrlToBlob(pdf.output('datauristring',{filename: 'A4.pdf'})));//140ms Base64 数据转 Blob //link.href = window.URL.createObjectURL(pdf.output('blob',{filename: 'A4.pdf'}));//77ms Base64 数据 link.href = pdf.output('bloburi');//77ms 直接输出Blob 链接 //link.href = //pdf.output('pdfobjectnewwindow',{filename: 'A41.pdf'});//弹出对象窗口,无用 //link.href = pdf.output('dataurl',{filename: 'A4.pdf'});//数据链接无用 //pdf.output('pdfjsnewwindow',{filename: 'A42.pdf'});//弹出窗口 //pdf.output('dataurlnewwindow',{filename: 'A43.pdf'});//弹出窗口,无用 link.download ="A41.pdf"; link.text='点击这里下载';

经过对比,发现在不考虑后台生成 pdf 文件的情况下,只有生成 Blob 链接适合用于手机前台下载,但只有华为自带浏览器支持下载 blob 链接而且还必须用华为浏览器打开那个网页才行,QQ、百度等均不支持下载 blob 链接。

jspdf 中原例子里面使用的是 iframe.src = pdf.output('datauristring'); 方式,返回的是 Base64 编码的 pdf 数据,可以使用参考资料 3 中的 convertBase64UrlToBlob 转换为 Blob 链接,分析 jspdf 的 Out 方法后发现其可直接输出 Blob 链接,那么就暂时用不到参考资料 3 中的方法了。

最终决定,由网页 JS 生成 Blob 链接给 A 标签,在手机端点击这个 A 标签下载为 pdf 文档,并打开。

(刚刚写了一大段,按了下 Ctrl+z 就全没了,草稿也没了,是要我重新梳理下吗???)

那么,就重新梳理,整理一下解决方法好了,零碎的分析就不写了。

那么,项目解决方案步骤开始:

1、已有可实用的动态 pdf 生成方案,详见: html转pdf文件下载之最合理的方法支持中文_jessezappy的博客-CSDN博客 2、安卓编辑器中建立 Layout ,添加 WebView : 3、extends Activity 将上面的 WebView 赋值给变量(实例化?),并设置其大堆属性,以扩展其功能: WebView mWebView;

onCreate 中赋值 mWebView:

mWebView=(WebView)findViewById(R.id.webshow); setWebStyle();

设置 mWebView 属性及重写其部分动作,扩展功能:

@SuppressLint("SetJavaScriptEnabled") private void setWebStyle() { WebSettings webseting = mWebView.getSettings(); webseting.setAppCachePath(getApplicationContext().getCacheDir().getAbsolutePath()); webseting.setUseWideViewPort(true); webseting.setLoadWithOverviewMode(true); //webseting.setPluginState(WebSettings.PluginState.ON); webseting.setDomStorageEnabled(true);//最重要的方法,一定要设置,这就是出不来的主要原因 //webseting.setDomStorageEnabled(true); webseting.setSupportZoom(true); webseting.setDefaultTextEncodingName("utf-8"); /* 下载blob准备 */ webseting.setJavaScriptEnabled(true); webseting.setJavaScriptCanOpenWindowsAutomatically(true); /***********************/ mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);// 去掉底部和右边的滚动条 mWebView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY); // 去掉底部和右边的滚动条 mWebView.requestFocus(); webseting.setCacheMode(WebSettings.LOAD_DEFAULT); // 默认使用缓存 // webseting.setCacheMode(WebSettings.LOAD_NO_CACHE); //默认不使用缓存! webseting.setAppCacheMaxSize(1024*1024*20);//设置缓冲大小,便于第一次缓存字体,否则每次下载字体需时太长。 String appCacheDir=this.getApplicationContext().getDir("cache",Context.MODE_PRIVATE).getPath(); webseting.setAppCachePath(appCacheDir); webseting.setAllowFileAccess(true); webseting.setAppCacheEnabled(true); webseting.setCacheMode(WebSettings.LOAD_DEFAULT|WebSettings.LOAD_CACHE_ELSE_NETWORK); //webview 动作重写 mWebView.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return false;// false 显示frameset, true 不显示Frameset ,内嵌页面 //return true; } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { //有页面跳转时被回调 //dialog = ProgressDialog.show(webxj.this,null,"数据加载中,请稍侯..."); super.onPageStarted(view, url,favicon); } @Override public void onPageFinished(WebView view, String url) { //页面跳转结束后被回调 //dialog.dismiss(); super.onPageFinished(view, url); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl);//错误处理+ description Toast.makeText(webxj.this, "错误:请检查网络链接! " , Toast.LENGTH_SHORT).show(); dialog.dismiss(); new Handler().postDelayed(new Runnable() { public void run() { mWebView.loadUrl(urlw); //显示等待画面 } }, 500); } }); //禁用webview右键功能:长按 mWebView.setOnLongClickListener(new OnLongClickListener(){ public boolean onLongClick(View v) { // TODO Auto-generated method stub return true; } }); //blob //下载支持,A 标签内需 download mWebView.setDownloadListener(new MyWebViewDownLoadListener()); // 设置 WebView 下载监听器 mWebView.addJavascriptInterface(mDownloadBlobFileJSInterface, "Android"); // Blob 下载 js 定义 mDownloadBlobFileJSInterface.setDownloadGifSuccessListener(new openDownloadfile()); // Blob 下载完成后监听器 } 4、引入参考资料 1 的方法,定义下载完成监听器

注:上面步骤 3 中已正确设置下载完成监听器:

mDownloadBlobFileJSInterface.setDownloadGifSuccessListener(new openDownloadfile()); //下载完成后监听器

参考资料 1 中的:

mDownloadBlobFileJSInterface.setDownloadGifSuccessListener(absolutePath -> Toast.makeText(MainActivity.this,"下载成功,在Download目录下",Toast.LENGTH_SHORT).show());

这句有问题,absolutePath 未定义,怀疑其是抄来的代码,没抄全,分析发现其为定义下载完成监听器,那么,就按照重写 DownloadListener 的定义,自己定义一个监听器,用于下载完成后的处理,如调用外部程序打开:

/* 定义下载完成事件监听器 */ private class openDownloadfile implements DownloadGifSuccessListener { public void ondownloadGifSuccess(String gifFile){ String fileName=gifFile.substring(gifFile.lastIndexOf("/")+1); String directory=gifFile.substring(0,gifFile.lastIndexOf("/")+1); // System.out.println("11.下载成功,fileName:"+fileName); // System.out.println("12.下载成功,directory:"+directory); File File = new File(directory,fileName); Intent intent = getFileIntent(File); startActivity(intent); //调用万部程序打开 // System.out.println("13.下载成功,在Download目录下:"+gifFile); } } public Intent getFileIntent(File file){ // Uri uri = Uri.parse("http://m.ql18.com.cn/hpf10/1.pdf"); Uri uri = Uri.fromFile(file); String type = getMIMEType(file); // Log.e("tag", "type="+type); Intent intent = new Intent("android.intent.action.VIEW"); intent.addCategory("android.intent.category.DEFAULT"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(uri, type); return intent; } private String getMIMEType(File f){ String type=""; String fName=f.getName(); // * 取得扩展名 * / String end=fName.substring(fName.lastIndexOf(".")+1,fName.length());//.toLowerCase(); Locale loc = Locale.getDefault();//可以去掉这步 end=end.toLowerCase(loc); // * 依扩展名的类型决定MimeType * / if(end.equals("pdf")){ type = "application/pdf";// } else if(end.equals("m4a")||end.equals("mp3")||end.equals("mid")|| end.equals("xmf")||end.equals("ogg")||end.equals("wav")){ type = "audio/*"; } else if(end.equals("3gp")||end.equals("mp4")){ type = "video/*"; } else if(end.equals("jpg")||end.equals("gif")||end.equals("png")|| end.equals("jpeg")||end.equals("bmp")){ type = "image/*"; } else if(end.equals("apk")){ // * android.permission.INSTALL_PACKAGES * / type = "application/vnd.android.package-archive"; } // else if(end.equals("pptx")||end.equals("ppt")){ // type = "application/vnd.ms-powerpoint"; // }else if(end.equals("docx")||end.equals("doc")){ // type = "application/vnd.ms-word"; // }else if(end.equals("xlsx")||end.equals("xls")){ // type = "application/vnd.ms-excel"; // } else{ // // * 如果无法直接打开,就跳出软件列表给用户选择 * / type="*/*"; //因注释多加了一个空格 } return type; }

其中 getFileIntent 和 getMIMEType 来自参考资料 2 。

参考资料 1 中的 DownloadBlobFileJSInterface 可直接使用,等下 获取 Blob 文件名时才需修改其转换 Base64 及保存过程。

5、获取 Blob 链接文件名及类型

参考资料 1 中,是无法取得 Blob 文件名及类型的,那么就要用个变通的方法:调用网页预设的 JS 中的 getDname 函数,获取文件名,文件名已事先由 .html 方法的回调函数中预设。

// 网页中已由 link.download ="A41.pdf"; 设置文件名 pdf.html(document.getElementById('pdfx'), { // 只有 addFileToVFS 方法添加的字体才能用于 .html 方法 callback: function (pdf) { d = new Date();timex=d.getTime(); var link = document.getElementById('linklink'); link.target = '_blank'; link.href = pdf.output('bloburi');//77ms link.download ="A41.pdf"; link.text='点击这里下载'; d = new Date(); timex=d.getTime()-timex; console.log('总用时'+timex + "ms"); //link.click(); //pdf.save("A4.pdf");//自动下载 //getDname(link.href); } }); // 用于 安卓 WebView 调用获取 download 属性的文件名 function getDname(s){ var a=document.getElementsByTagName("a"); var h=""; for(let i=0;i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有