uni小程序, 嵌入原生app, 文件下载到指定目录的实现方式
- 实现该功能的时候, 两个问题需要解决, 1.uni.openDocument,只能打开几个常规后缀的文件 2. 下载文件下到了沙盒里面. _download开头的内部路径, 用户下次找不到了
为解决这两个问题, 我也是做了很多尝试, 现在将最终实现方式记录分享, 希望对你有些帮助
- 使用 plus.runtime.openFile 打开文件, 没有文件格式限制, 遇到打不开的文件时, 会有异常回调可以使用, 提示用户安装相关软件即可.
- 利用plus.downloader.createDownload 下载文件文档上说明只能保存到_dowloads, _doc,等几个固定开头的沙盒文件夹里, 所以只能扩展原生Moudle, 将文件移动到Download文件夹下
一下是代码部分,仅供参考
- 原生部分代码, 自定义Module,复制文件,一定要提前获取到用户的读写数据的权限!!!, 否则会提示权限问题, 文件无法复制
public class UniUesOaHeModule extends UniModule {
/**
* 输出日志
*/
@UniJSMethod(uiThread = false)
public void uniLog(String s) {
XLog.debug(s);
}
/**
* 沙盒文件移入媒体库
*/
@UniJSMethod(uiThread = false)
public void scanIntoMedia(String filePath, UniJSCallback callback) {
if (StringUtil.isEmpty(filePath)) {
if (callback != null) {
JSONObject data = new JSONObject();
data.put("code", "error");
data.put("message", "文件路径不能为空");
callback.invokeAndKeepAlive(data);
}
}
if (mUniSDKInstance != null) {
String[] split = filePath.split("/");
String fileName = split[split.length -1];
String targetPath = "/storage/emulated/0/Download/" + fileName;
FileUtil fileUtil = FileUtil.INSTANCE;
XLog.debug("开始转义目录");
XLog.debug("由:[ " + filePath + " ] 转移至 : [ "+ targetPath + " ]");
//tod
fileUtil.copyFileWithFileChannel(new File(filePath), new File(targetPath));
//对于小米来说, 拷贝到Downloads文件夹下, 系统就会自动扫描到了, 不知道其他机型怎么样
//图片等常规文件,能出现在 `最近`里, dwg在Download里能找到, 但是不会在 `最近` 里显示,可能是系统原因
Context context = mUniSDKInstance.getContext();
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.parse(targetPath);
XLog.debug("将要扫描的地址是: " + contentUri.getPath());
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);
XLog.debug("发送扫描指令");
if (callback != null) {
JSONObject data = new JSONObject();
data.put("code", "success");
data.put("message", "添加到Download目录成功");
data.put("filePathAfterMove", "file:/"+ targetPath);
callback.invokeAndKeepAlive(data);
}
}else{
XLog.debug("实例不存在");
if (callback != null) {
JSONObject data = new JSONObject();
data.put("code", "error");
data.put("message", "实例不存在");
callback.invokeAndKeepAlive(data);
}
}
/**
* 下面的不管用, 可能是只能扫描固定的几个媒体库路径, 上面的直接复制到Downloads下, 不用扫描都能发现
*/
//
// if (StringUtil.isEmpty(filePath)) {
// if (callback != null) {
// JSONObject data = new JSONObject();
// data.put("code", "error");
// data.put("message", "文件路径不能为空");
// callback.invokeAndKeepAlive(data);
// }
// }
// if (mUniSDKInstance != null) {
// Context context = mUniSDKInstance.getContext();
// try {
// MediaScannerConnection.scanFile(context, new String[]{filePath}, null,
// new MediaScannerConnection.OnScanCompletedListener() {
// public void onScanCompleted(String path, Uri uri) {
// XLog.debug("扫描的路径是: " + path + ":");
// XLog.debug("返回的uri: " + uri);
// if (callback != null) {
// JSONObject data = new JSONObject();
// data.put("code", "success");
// data.put("message", "媒体库扫描完成!!!!");
// callback.invokeAndKeepAlive(data);
// }
// }
// });
// } catch (Exception e) {
// e.printStackTrace();
// }
// } else {
// if (callback != null) {
// JSONObject data = new JSONObject();
// data.put("code", "error");
// data.put("message", "移入媒体库失败, 找不到mUniSDKInstance实例");
// callback.invokeAndKeepAlive(data);
// }
// }
}
}
- FileUtil, 只贴这一个复制文件的函数
/**
* 复制文件
*/
fun copyFileWithFileChannel(fileSource: File, fileDest:File) {
var fi: FileInputStream? = null
var fo: FileOutputStream? = null
var `in`: FileChannel? = null
var out: FileChannel? = null
try {
fi = FileInputStream(fileSource)
fo = FileOutputStream(fileDest)
`in` = fi.channel//得到对应的文件通道
out = fo.channel//得到对应的文件通道
`in`!!.transferTo(0, `in`.size(), out)//连接两个通道,并且从in通道读取,然后写入out通道
} catch (e: IOException) {
e.printStackTrace()
} finally {
try {
fi!!.close()
`in`!!.close()
fo!!.close()
out!!.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
别忘记注册组件
UniSDKEngine.registerModule(“UniUesOaHeModule”, UniUesOaHeModule::class.java)
uni小程序代码, download2是最终使用的方法, 其他的是测试时使用的
<template>
<!-- 这是图纸展示页面 -->
<page-head title="无匹配数据" v-if="designDrawingArray.length == 0"></page-head>
<view v-else>
<!-- {{designDrawingArray}} -->
<view v-for="(item, index) in designDrawingArray" :key="item.id">
<!-- {{ item.name }} - {{ item.remark }} -->
<uni-card :title="item.name" :sub-title="item.remark">
<view v-if="item.attachmentList.length > 0">
<uni-list>
<uni-list-item v-for="(attachmentItem, attachmentIndex) in item.attachmentList"
:key="attachmentItem.id" :title="attachmentItem.attachName"
@click="download2(attachmentItem.attachUrl,attachmentItem.attachSize)" showArrow link>
</uni-list-item>
</uni-list>
</view>
<view v-else>
暂无图纸
</view>
</uni-card>
</view>
</view>
</template>
<script>
import config from "@/common/config.js"
var uniUesOaHeModule = uni.requireNativePlugin("UniUesOaHeModule")
export default {
data() {
return {
designDrawingArray: []
}
},
onLoad(e) {
var contractType = e.contractType
var contractId = e.contractId
this.queryHeDesignDrawingWithAttachmentList(contractType, contractId)
},
methods: {
queryHeDesignDrawingWithAttachmentList(contractType, contractId) {
this.$http.post({
url: '/engineering/queryHeDesignDrawingWithAttachmentList.action',
data: {
contractType: contractType,
contractId: contractId
},
success: (res) => {
console.log(res)
if (res.status == 200) {
this.designDrawingArray = res.data
}
}
})
},
// 该方法不再使用, openDocument只能打开常用格式的文件, 不能打开dwg
downLoadAttachment(url) {
var downloadUrl = config.serverUrl + url
console.log("下载地址---" + downloadUrl)
uni.showModal({
title: '提示',
content: '确定下载该图纸?',
success: function(res) {
if (res.confirm) {
uni.showLoading({
title: '下载中'
})
var self = this
uni.downloadFile({
url: downloadUrl,
success: (res) => {
//保存到本地
console.log("下载成功,临时路径是---" + res.tempFilePath)
uni.saveFile({
tempFilePath: res.tempFilePath, //文件的临时路径
success: function(res) {
const savedFilePath = res.savedFilePath;
uni.showToast({
title: '将临时文件保存完成,保存的地址为:' +
savedFilePath,
icon: 'none'
});
uni.hideLoading();
uni.showModal({
title: '提示',
content: '打开下载文件?',
success: function(res) {
// 打开文件
if (res.confirm) {
uni.showToast({
title: '点击了确定打开文件',
icon: 'none'
});
uni.openDocument({
filePath: savedFilePath,
showMenu: true,
success: function(
res
) {
console
.log(
'打开文档成功'
);
uni.showToast({
title: '打开文档成功',
icon: 'none'
});
},
fail: function(
res
) {
console
.log(
'打开文档失败'
);
uni.showToast({
title: '打开文档失败',
icon: 'none'
});
}
});
}
}
})
},
fail: function(err) {}
});
},
fail: (err) => {
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
})
}
}
});
},
download2(url,fileSize) {
var logger = this.$log
var downloadUrl = config.serverUrl + url
logger(uniUesOaHeModule, "下载地址---" + downloadUrl)
//测试方便,使用390576, 正式使用下面50M
// if(fileSize > 1000 * 1000 * 50){
if(fileSize > 390576){
logger(uniUesOaHeModule, "文件大于50M,使用浏览器下载: "+ fileSize)
plus.runtime.openURL(downloadUrl)
}else{
logger(uniUesOaHeModule, "文件小于50M: "+ fileSize)
var pathArray = url.split('/')
var fileName = pathArray[pathArray.length - 1]
uni.showLoading({
title: '下载中'
})
let dtask = plus.downloader.createDownload(downloadUrl, {
//利用保存路径,实现下载文件的重命名,文档上说明只能保存到_dowloads, _doc,等几个固定开头的沙盒文件夹里,
// filename:localFile
}, function(d, status) {
uni.hideLoading();
if (status == 200) {
//下载成功,d.filename是文件在保存在本地的相对路径,使用下面的API可转为平台绝对路径
logger(uniUesOaHeModule, "下载完成了,现在要去打开文件,文件地址也就是d.fileName: " + d.filename)
let fileSaveUrl = plus.io.convertLocalFileSystemURL(d.filename);
logger(uniUesOaHeModule, "将d.fileName 沙盒文件路径转化为android平台的路径: " + fileSaveUrl)
//这样下载打开文件, 除非用户自己操作或移动到自己知道的文件夹下, 下次他就找不到了, 因此调用原生Module,将这个文件放到/Download下,下次能看到
uniUesOaHeModule.scanIntoMedia(fileSaveUrl,
(ret) => {
console.log(JSON.stringify(ret))
if(ret.code == 'success'){
//文件转移到Download目录成功了
plus.runtime.openFile(ret.filePathAfterMove,{},function(error){
logger(uniUesOaHeModule,"打开文件有问题: "+ JSON.stringify(error))
uni.showToast({
title: '没有能打开该文件的软件,请安装',
icon: 'none',
duration: 3000
});
});
}
})
} else {
uni.showToast({
title: '下载失败',
icon: 'none',
duration: 3000
});
plus.downloader.clear(); //清除下载任务
}
})
dtask.addEventListener("statechanged", function(task, status) {
logger(uniUesOaHeModule, "task:" + JSON.stringify(task) + "===========status" + status)
switch (task.state) {
case 2:
break;
case 3:
let prg = parseInt((parseFloat(task.downloadedSize) / parseFloat(task.totalSize)) *100);
// logger(uniUesOaHeModule, prg + "%")
break;
case 4:
break;
}
}, false);
dtask.start();
}
},
test(url, fileSize){
var logger = this.$log
var downloadUrl = config.serverUrl + url
logger(uniUesOaHeModule, "下载地址---" + downloadUrl)
// uni.showToast({
// title: '测试打开外部文件',
// icon: 'none'
// });
// plus.runtime.openFile("file://storage/emulated/0/Download/e96cca6878b14911940c4ad9db8e1ff7_a(9).jpg",{},function(error){
// // logger(uniUesOaHeModule,"打开文件出错: "+ JSON.stringify(error))
// console.log(JSON.stringify(error))
// uni.showToast({
// title: '1111111111',
// icon: 'none'
// });
// });
//大于50M, 建议使用浏览器下载
if(fileSize > 1000 * 1000 * 50){
logger(uniUesOaHeModule, "文件大于50M: "+ fileSize)
}else{
logger(uniUesOaHeModule, "文件小于50M: "+ fileSize)
}
plus.runtime.openURL(downloadUrl)
}
}
}
</script>
<style>
</style>