问题描述:我是将我的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中容易配置,打开外部浏览器即可下载,不需要用户再进行登录操作。