| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- /**
- * IM 平台全端通用 Web-SDK (im-sdk.js)
- * 作用:统一子应用调用原生设备能力的接口,防止 WebView 直接调用引发的内存崩溃 (OOM)。
- * 版本:v1.0.0
- */
- (function(window, document) {
- // ---------------------------------------------------------
- // 1. 环境准备:确保注入 DCloud 官方 Webview 通信 SDK
- // ---------------------------------------------------------
- if (!window.uni || !window.uni.webView) {
- var script = document.createElement('script');
- script.type = 'text/javascript';
- // 动态引入 uni.webview.js
- script.src = 'https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js';
- document.head.appendChild(script);
- }
- // ---------------------------------------------------------
- // 2. 通信基建:回调注册与响应机制
- // ---------------------------------------------------------
- window.imBridgeCallbacks = {};
- /**
- * 供 App 壳子 (uni-app) 调用的全局方法,用于将原生执行结果塞回给 H5
- * @param {string} callbackId - 唯一回调标志
- * @param {object} data - 原生层返回的数据
- */
- window.imBridgeInvoke = function(callbackId, data) {
- if (window.imBridgeCallbacks[callbackId]) {
- // 执行业务代码中传入的 success/fail 回调
- window.imBridgeCallbacks[callbackId](data);
- // 执行完毕,销毁内存中的回调函数
- delete window.imBridgeCallbacks[callbackId];
- }
- };
- /**
- * 内部核心方法:向 App 壳子发送指令
- */
- function sendCommandToApp(action, params, callback) {
- var callbackId = 'cb_' + action + '_' + Date.now() + '_' + Math.floor(Math.random() * 1000);
-
- if (typeof callback === 'function') {
- window.imBridgeCallbacks[callbackId] = callback;
- }
- // 确保 uni.webView 已经加载完毕再发送
- var checkAndSend = function() {
- if (window.uni && window.uni.webView) {
- window.uni.webView.postMessage({
- data: Object.assign({ action: action, callbackId: callbackId }, params)
- });
- } else {
- setTimeout(checkAndSend, 100); // 轮询等待 SDK 加载
- }
- };
- checkAndSend();
- }
- // ---------------------------------------------------------
- // 3. 平台标准 API (供子应用开发者主动调用)
- // ---------------------------------------------------------
- window.imSDK = {
- /**
- * 调起原生相机拍照或选择相册 (返回本地路径)
- */
- chooseImage: function(options) {
- options = options || {};
- sendCommandToApp('chooseImage', {
- count: options.count || 1,
- sourceType: options.sourceType || ['camera', 'album']
- }, options.success);
- },
- /**
- * 调起原生相机录像或选择视频 (返回本地路径)
- */
- chooseVideo: function(options) {
- options = options || {};
- sendCommandToApp('chooseVideo', {
- sourceType: options.sourceType || ['camera', 'album'],
- maxDuration: options.maxDuration || 60,
- camera: options.camera || 'back'
- }, options.success);
- },
- /**
- * 选择系统文件 (返回本地路径)
- */
- chooseFile: function(options) {
- options = options || {};
- sendCommandToApp('chooseFile', {
- count: options.count || 1,
- extension: options.extension || []
- }, options.success);
- },
- /**
- * 【核心】将本地路径委托给原生 App 进行上传,防止大文件导致 H5 内存溢出
- */
- uploadFile: function(options) {
- options = options || {};
- if (!options.url || !options.filePath) {
- console.error('[imSDK] uploadFile 缺少必填参数: url 或 filePath');
- return;
- }
- sendCommandToApp('uploadFile', {
- url: options.url,
- filePath: options.filePath,
- name: options.name || 'file',
- formData: options.formData || {},
- header: options.header || {}
- }, options.success);
- }
- };
- // ---------------------------------------------------------
- // 4. 终极防线:全局 DOM 劫持 (保护老旧/不规范的 H5 页面)
- // ---------------------------------------------------------
- document.addEventListener('click', function(event) {
- var target = event.target;
- // 锁定目标:点击了 <input type="file">
- if (target.tagName && target.tagName.toUpperCase() === 'INPUT' && target.type === 'file') {
-
- // 粗略判断是否在 App 内 (如果不在,则放行,走普通浏览器逻辑)
- // 建议:在你的 App 壳子 webview 加载时,给 userAgent 拼上一个自定义标识,如 'MyIMPlatform'
- var isInsideApp = /uni-app/i.test(navigator.userAgent);
-
- if (isInsideApp) {
- // 【核心防御】拦截浏览器默认调起系统相册/相机的行为
- event.preventDefault();
- event.stopPropagation();
-
- console.log('[imSDK] 已拦截危险的原生 input 调用,正转交底层处理...');
- // 判断 input 是想选图片还是视频
- var isVideo = target.accept && target.accept.indexOf('video') !== -1;
- var action = isVideo ? 'chooseVideo' : 'chooseImage';
- var sourceType = target.hasAttribute('capture') ? ['camera'] : ['camera', 'album'];
- // 强制转为调用我们的标准 API
- window.imSDK[action]({
- sourceType: sourceType,
- success: function(res) {
- // 注意:这里拿到的是 App 传回的本地路径 (tempFilePath)。
- // 如果是老旧 HTML 页面,强行重写 DOM 属性可能受限于浏览器跨域安全策略。
- // 这里派发一个自定义事件,供高级开发者捕获;普通业务建议直接改用 imSDK.chooseImage。
- console.log('[imSDK] 劫持执行完毕,获取到原生路径:', res);
-
- var customEvent = new CustomEvent('im-file-choosed', { detail: res });
- target.dispatchEvent(customEvent);
- }
- });
- }
- }
- }, true); // true: 捕获阶段拦截,最高优先级
- })(window, document);
|