问题描述:我是将我的web项目使用hbuilder打包成apk安装在手机上,然后使用下载功能的时候,跳转到报错页面,但是在pc端浏览器和手机浏览器上没有问题,报错页面如下:
其实这是因为使用a链接发送请求下载文件时,默认会有短暂跳转到空白页的行为,但是我后面使用form,以及iframe方式下载,虽然可以下载,但是我在手机上找不到文件位置,这样,软件又需要自动打开刚才下载的文件,不然用户找起来很费劲。 于是,我搜集了很多资料终于完美解决了这个问题。 思路:在下载文件,如点击a链接时,让它跳转到外部浏览器上,使用外部浏览器进行托管下载,这样,能使得下载功能达到最大人性化。 首先介绍一个api:
plus.runtime.openURL(url) //url:使用外部浏览器让用户下载的页面地址 //如http://101.40.131.45:8080/downfile/ //这个plus是Hbuilder提供的第三方插件,只有在打包的app上面才有效 //在pc端的浏览器上是不存在的,所以提供下方代码,判断当前设备类型 //检测用户当前的设备 var system = { win: false, mac: false, xll: false, ipad:false }; //检测平台 var p = navigator.platform; system.win = p.indexOf("Win") == 0; system.mac = p.indexOf("Mac") == 0; system.x11 = (p == "X11") || (p.indexOf("Linux") == 0); system.ipad = (navigator.userAgent.match(/iPad/i) != null)?true:false; //当用户的设备是win时, if(system.win){ //xxxxx }else{ //xxxxxx }下面是我使用的代码,真实项目中的:
//html标签: ///down/downProjFile/1中的1是要下载文件存在数据库中的id号 //其它字段还有文件的存储路径等,反正通过id号,我的后台就能够知道下载哪个文件 <a href="/down/downProjFile/1" fileid="1" class="downABtn btn btn-warning">下载</a> //js代码: $(".downABtn").click(function () { var system = { win: false, mac: false, xll: false, ipad:false }; //检测平台 var p = navigator.platform; system.win = p.indexOf("Win") == 0; system.mac = p.indexOf("Mac") == 0; system.x11 = (p == "X11") || (p.indexOf("Linux") == 0); system.ipad = (navigator.userAgent.match(/iPad/i) != null)?true:false; if(system.win){ return true; } const fileid = $(this).attr("fileid"); plus.runtime.openURL('https://101.210.165.250:8443/wzq-0.0.1-SNAPSHOT/down/downFilePage/' + fileid); //阻止a链接的默认行为 return false; })Java的后台代码:(根据你使用的语言进行对比即可)
@Controller @RequestMapping("/down") public class DownLoadController { //存储文件的目录 @Value("${system.filePath}") private String filePath; @Autowired private ProjFileService projFileService; //跳转到下载页面的请求,即是使用plus跳转的请求 @RequestMapping("/downFilePage/{fileId}") public String downFilePage(@PathVariable("fileId")Integer fileId, Map<String, Object> map){ map.put("fileId", fileId); return "proj/downFilePage"; } //下载文件的请求 @RequestMapping("/downProjFile/{fileId}") public void downProjFIle(@PathVariable("fileId") Integer fileId, HttpServletResponse res) throws Exception{ ProjFile projFile = projFileService.getProjFileById(fileId); String fileName = projFile.getName(); //下面的代码是通用的 res.setHeader("content-type", "application/*"); res.setContentType("application/*"); res.setHeader("Content-Disposition", "attachment; filename=" + new String(fileName.getBytes("utf-8"), "iso8859-1")); byte[] buff = new byte[1024]; BufferedInputStream bis = null; OutputStream os = null; try { os = res.getOutputStream(); //这里的 filePath + fileName 可以改成你文件存储的位置 bis = new BufferedInputStream(new FileInputStream( new File(filePath + fileName ))); int i = bis.read(buff); while (i != -1) { os.write(buff, 0, buff.length); os.flush(); i = bis.read(buff); } } catch ( IOException e ) { e.printStackTrace(); } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } } } }下载页面我用的thymeleaf,做的很简单,只有一个确认下载按钮,代码如下:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta charset="UTF-8"> <title>Title</title> </head> <script th:src="@{/webjars/jquery/3.0.0/jquery.js}" type="text/javascript"></script> <link rel="stylesheet" th:href="@{/asserts/css/bootstrap.min.css}" /> <body> <center> <a th:href="@{'/down/downProjFile/' + ${fileId}}" class="btn btn-warning btn-lg" id="downABtn">确认下载</a> </center> </body> </html>页面效果:(这里的页面就是使用外部浏览器打开的) 为什么要把下载文件和转到下载页面的两个请求放到独立的Controller中,因为我项目中涉及到登录,有拦截器配置,放到独立的Controller中容易配置,打开外部浏览器即可下载,不需要用户再进行登录操作。