需求
uniapp兼容android app,pc,h5的文件选择并上传功能。
需要支持拍照和相册选择,以及选择其他类型文件上传~
实践过程和问题
开始使用uni-file-picker组件
以为很顺利,android模拟器测试……
忽略了平台兼容性提示~😭
Get:上传图片或拍照,使用uniapp自带的uni.chooseImage即可……
还需要上传非媒体问题(图片、音频、影片)。本以为uni.chooseFile就OK了~
But
提示
uniapp提示:Android端可以使用Native.js
- App端如需选择非媒体文件,可在插件市场搜索文件选择,其中Android端可以使用Native.js,无需原生插件,而iOS端需要原生插件。
- App端如果想选择下载到_doc、_downloads、_documents等plus.io控制的目录下的文件,可通过plus.io Api,自己做选择框。
官方提示到此为止~
AI求解
于是,开始了漫长的AI问答……
deepseek、豆包、文心全问一遍~(其实内核是不是都deepseek🙂)
都”快速“给出答案代码,然而”打包“测试已经没反应;好一点的依旧只能选图片~
举一个栗子~
问:Android端使用Native.js进行选择文件上传
答:
1. 配置权限
在
manifest.json
文件中配置文件读取权限:{ "plus": { "permission": { "readExternalStorage": { "desc": "读取设备外部存储空间的文件", "android": { "name": "android.permission.READ_EXTERNAL_STORAGE" } } } } }
2. 编写选择文件的 Native.js 代码
在页面的
<script>
标签内或单独的 JS 文件中编写选择文件的逻辑:function pickFile(callback) { const Intent = plus.android.importClass('android.content.Intent'); const intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); // 设置选择所有类型文件 intent.setType('*/*'); plus.android.startActivityForResult(intent, function (intentData) { if (intentData) { const uri = intentData.getData(); const ContentResolver = plus.android.importClass('android.content.ContentResolver'); const cursor = plus.android.invoke(plus.android.runtimeMainActivity, 'getContentResolver') .query(uri, ['_data'], null, null, null); if (cursor.moveToFirst()) { const columnIndex = cursor.getColumnIndex('_data'); const filePath = cursor.getString(columnIndex); cursor.close(); callback(filePath); } } }); }
3. 编写上传文件的代码
结合
uni.uploadFile
方法来上传选择的文件:<template> <view class="container"> <button @click="pickAndUpload">选择并上传文件</button> </view> </template> <script> export default { methods: { pickAndUpload() { pickFile((filePath) => { if (filePath) { this.uploadFile(filePath); } else { uni.showToast({ title: '未选择文件', icon: 'none' }); } }); }, uploadFile(filePath) { uni.uploadFile({ url: 'https://your - server - url.com/api/upload', // 替换为实际的上传接口地址 filePath: filePath, name: 'file', success: function (res) { console.log('文件上传成功', res.data); uni.showToast({ title: '文件上传成功', icon:'success' }); }, fail: function (err) { console.log('文件上传失败', err); uni.showToast({ title: '文件上传失败', icon: 'none' }); } }); } } }; </script> <style scoped> .container { padding: 20px; } </style>
很详细~
然而只有一个问题:不work!
o(╯□╰)o
正确答案
最后还在uniapp的论坛上找到了答案,已经通过android app测试,可用~
使用原生的Native.js调用📱平台的文件管理器,选择文件并向上传文件的相关方法传递所选文件的正确路径。
如果会安卓原生编程,代码so easy。
// from DCloud
window.PickFile = function(callback,acceptType){
function ip(obj){
plus.android.importClass(obj);
return obj;
}
if(plus.os.name == 'Android' && typeof callback == 'function'){
var CODE_REQUEST = 1000;
var context = plus.android.runtimeMainActivity();
ip(context);
var Intent = plus.android.importClass('android.content.Intent');
var intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
if(acceptType){
intent.setType(acceptType);
}else{
intent.setType("*/*");
}
context.onActivityResult = function(requestCode,resultCode,intentData){
if(requestCode == CODE_REQUEST){
if(intentData){
var uriValue = intentData.getData();
plus.android.importClass(uriValue);
var scheme = uriValue.getScheme();
if(scheme == 'content'){//还需要进行数据库查询,一般图片数据
var cursor = ip(context.getContentResolver()).query(uriValue,['_data'], null, null, null);
if(cursor){
ip(cursor).moveToFirst();
var columnIndex = cursor.getColumnIndex('_data');
picturePath = cursor.getString(columnIndex);
cursor.close();
callback(picturePath);//返回文件路径
}
}else if(scheme == 'file'){
callback(uriValue.getPath());//返回文件路径
}
}else{
callback(null);
}
}
}
context.startActivityForResult(intent,CODE_REQUEST);
}
}
实现
为了h5还是先判断下环境
const BaseInfo = uni.getAppBaseInfo();
BaseInfo.uniPlatform==="app”的话用,h5还是uni.chooseFile
function ip(obj){
plus.android.importClass(obj);
return obj;
}
uploadFileInit(){
const CODE_REQUEST = 500;
let context = plus.android.runtimeMainActivity();
ip(context);
let Intent = plus.android.importClass('android.content.Intent');
let intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
context.onActivityResult = function(requestCode,resultCode,intentData){
if(requestCode == CODE_REQUEST){
if(intentData){
var uriValue = intentData.getData();
plus.android.importClass(uriValue);
var scheme = uriValue.getScheme();
if(scheme == 'content'){//还需要进行数据库查询,一般图片数据
var cursor = ip(context.getContentResolver()).query(uriValue,['_data'], null, null, null);
if(cursor){
ip(cursor).moveToFirst();
var columnIndex = cursor.getColumnIndexOrThrow('_data');
try{
var filePath = cursor.getString(columnIndex);
_this.filePath = filePath;
cursor.close();
// _this.调用上传接口的方法(filePath, ‘文件类型’);
}catch(e){
}
}
}else if(scheme == 'file'){
// 路径 uriValue.getPath()
}
}else{
uni.showToast({
title: '选择文件失败',
icon: 'none'
});
}
}
}
context.startActivityForResult(intent,CODE_REQUEST);
}
找了好久~真机测试可行~
吐槽
uniapp这个是个坑,明明很多文件选择插件,都不兼容~
官方会推荐兼容的插件——当然:收费~
其他实现方式推荐
曲线救国:web-view
在 web-view 组件内可以使用 input 元素进行选择,使用表单或者 xhr 上传;
在插件市场搜索:全文件上传选择非原生
全文件上传选择非原生2.0版 - DCloud 插件市场