125 lines
3.6 KiB
JavaScript
125 lines
3.6 KiB
JavaScript
/**
|
||
* 文件打开工具 - 强制弹出应用选择器
|
||
*
|
||
* uni.openDocument 的 showMenu 和 plus.runtime.openFile 在真机上
|
||
* 都可能直接跳转默认应用。用 Android 原生 Intent.createChooser
|
||
* 强制弹出"选择应用"对话框。
|
||
*
|
||
* Android 7.0+ 会阻止 file:// URI 暴露给其他应用,需要禁用此限制。
|
||
*/
|
||
|
||
/**
|
||
* 根据文件扩展名获取 MIME 类型
|
||
*/
|
||
function getMimeType(filePath) {
|
||
const ext = filePath.substring(filePath.lastIndexOf('.') + 1).toLowerCase()
|
||
const mimeMap = {
|
||
'pdf': 'application/pdf',
|
||
'doc': 'application/msword',
|
||
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||
'xls': 'application/vnd.ms-excel',
|
||
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||
'ppt': 'application/vnd.ms-powerpoint',
|
||
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||
'txt': 'text/plain',
|
||
'html': 'text/html',
|
||
'htm': 'text/html',
|
||
'xml': 'text/xml',
|
||
'json': 'application/json',
|
||
'csv': 'text/csv',
|
||
'jpg': 'image/jpeg',
|
||
'jpeg': 'image/jpeg',
|
||
'png': 'image/png',
|
||
'gif': 'image/gif',
|
||
'bmp': 'image/bmp',
|
||
'webp': 'image/webp',
|
||
'mp4': 'video/mp4',
|
||
'mp3': 'audio/mpeg',
|
||
'wav': 'audio/wav',
|
||
'zip': 'application/zip',
|
||
'rar': 'application/x-rar-compressed',
|
||
'7z': 'application/x-7z-compressed',
|
||
'apk': 'application/vnd.android.package-archive'
|
||
}
|
||
return mimeMap[ext] || '*/*'
|
||
}
|
||
|
||
/**
|
||
* 打开文件,弹出应用选择器
|
||
* @param {string} filePath 本地文件路径
|
||
* @param {Function} onFail 打开失败时的回调
|
||
*/
|
||
export function openFile(filePath, onFail) {
|
||
// #ifdef APP-PLUS
|
||
try {
|
||
const main = plus.android.runtimeMainActivity()
|
||
const Intent = plus.android.importClass('android.content.Intent')
|
||
const Uri = plus.android.importClass('android.net.Uri')
|
||
const File = plus.android.importClass('java.io.File')
|
||
|
||
// Android 7.0+ 默认禁止 file:// URI,禁用此限制
|
||
if (plus.os.sdkInt >= 24) {
|
||
const StrictMode = plus.android.importClass('android.os.StrictMode')
|
||
StrictMode.disableDeathOnFileUriExposure()
|
||
}
|
||
|
||
const file = new File(filePath)
|
||
if (!file.exists()) {
|
||
throw new Error('文件不存在: ' + filePath)
|
||
}
|
||
|
||
const uri = Uri.fromFile(file)
|
||
const mimeType = getMimeType(filePath)
|
||
|
||
const intent = new Intent(Intent.ACTION_VIEW)
|
||
intent.setDataAndType(uri, mimeType)
|
||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||
|
||
// 核心:createChooser 每次必弹选择器,无视已设置的默认应用
|
||
const chooser = Intent.createChooser(intent, '选择应用打开')
|
||
main.startActivity(chooser)
|
||
|
||
console.log('openFile success - chooser shown')
|
||
} catch (e) {
|
||
console.error('openFile native failed, fallback:', e)
|
||
uni.openDocument({
|
||
filePath: filePath,
|
||
showMenu: true,
|
||
fail: typeof onFail === 'function' ? onFail : () => {}
|
||
})
|
||
}
|
||
// #endif
|
||
|
||
// #ifndef APP-PLUS
|
||
uni.openDocument({
|
||
filePath: filePath,
|
||
showMenu: true,
|
||
fail: typeof onFail === 'function' ? onFail : () => {}
|
||
})
|
||
// #endif
|
||
}
|
||
|
||
/**
|
||
* 下载文件并打开(弹出应用选择器)
|
||
* @param {string} url 远程文件 URL
|
||
* @param {Function} onFail 失败回调
|
||
*/
|
||
export function downloadAndOpen(url, onFail) {
|
||
uni.downloadFile({
|
||
url: url,
|
||
success: (res) => {
|
||
if (res.statusCode === 200) {
|
||
openFile(res.tempFilePath, onFail)
|
||
} else {
|
||
console.error('下载失败, statusCode:', res.statusCode)
|
||
if (typeof onFail === 'function') onFail()
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error('downloadFile fail:', err)
|
||
if (typeof onFail === 'function') onFail()
|
||
}
|
||
})
|
||
}
|