
uni.upload多文件上传测试180次均无法成功
uni.upload多文件上传测试180次均无法成功
文章大纲
- 问题来源
- 思路分析
- 测试经过
- 最终解决方案
问题来源
项目为了实现图片和视频多文件上传
思路分析
为了实现图片和视频多文件上传功能,经过5天,180次测试,我沿用以下三个思路进行测试。
1、我首先采用的是uni.uploadFile插件。(尝试接近50次次数)
2、其次采用的方法是原生数据连接方法,XMLHTTPrequest方法,失败(从50-100次测试)
3、发现依然失效,于是采用H5Plus,upLoader的方法,从100-180次测试,依然无效。
我最终面对自我信心缺失和无力的情况下,找到突破口,让后端重写多文件上传的功能,通过单文件依次上传,实现多文件上传功能。
为了纪念180次,一周的辛苦,写下文章,以免其他程序员也遇到同样的问题,和我一样白白做无用功。趁早让后端改代码,多文件变成单文件是最快的方式。
测试经过
核心问题在于,用Postman测试多文件上传功能是OK的,但是前端用的语法却失去效果。
所以我首先用的是uni.upLoadFile插件,看uni.upLoadFile插件文档,自己是不是对文档哪里理解错误,对于这个接口是否正确使用,语法有没有写错。因为这时候的报错是upload File fail ,
| 参数名 | 类型 | 必填 | 说明| 平台差异说明 |
| files | Array | 否 |需要上传的文件列表。使用 files 时,filePath 和 name 不生效 | 5 App |
1-30次测试,比较POSTman和Uniapp参数是否不同。比如检查file格式,相对还是绝对路径
30-50次测试,检查是否和内网相关联,因为我们公司是内网开发环境,后来发现并没有关系。
50次后,采用原生XmlHttpRequest写,依然无效。
80次后,采用uploader,我实在是精疲力竭,已经连续3天加班,每天从早测到晚,每次测试都毫无结果。
重新分析
所以我开始分析程序哪里出了了问题,再次判断分析为以下三个问题。
是否接口问题?用POSTMAN排除了。
是否语法问题?测了20次,派出了语法问题
是否方法问题?检查了前面两条路,(uni.uploade和xmlHttpRequest)我都发现死磕失败,最终走向第三条路——uploader
第100-130次:
fail to construct 'File:Iterator setter is not callable
这句话测试了30次,才发现是代码多了一个构造器new File
第130-180次
Uploader可以检测到上传过程中的数据问题,我检测了其中的数据,发现是已经上传了数据,但是结果却是失败的。
最终解决方案
我在180次测试结束后发现,多文件上传用uniapp多个路径始终走不通,得让后台配合把多文件接口改为单文件接口一对一传输才行。
这就是最终解决方案了。
uni.upload多文件上传测试180次均无法成功
文章大纲
- 问题来源
- 思路分析
- 测试经过
- 最终解决方案
问题来源
项目为了实现图片和视频多文件上传
思路分析
为了实现图片和视频多文件上传功能,经过5天,180次测试,我沿用以下三个思路进行测试。
1、我首先采用的是uni.uploadFile插件。(尝试接近50次次数)
2、其次采用的方法是原生数据连接方法,XMLHTTPrequest方法,失败(从50-100次测试)
3、发现依然失效,于是采用H5Plus,upLoader的方法,从100-180次测试,依然无效。
我最终面对自我信心缺失和无力的情况下,找到突破口,让后端重写多文件上传的功能,通过单文件依次上传,实现多文件上传功能。
为了纪念180次,一周的辛苦,写下文章,以免其他程序员也遇到同样的问题,和我一样白白做无用功。趁早让后端改代码,多文件变成单文件是最快的方式。
测试经过
核心问题在于,用Postman测试多文件上传功能是OK的,但是前端用的语法却失去效果。
所以我首先用的是uni.upLoadFile插件,看uni.upLoadFile插件文档,自己是不是对文档哪里理解错误,对于这个接口是否正确使用,语法有没有写错。因为这时候的报错是upload File fail ,
| 参数名 | 类型 | 必填 | 说明| 平台差异说明 |
| files | Array | 否 |需要上传的文件列表。使用 files 时,filePath 和 name 不生效 | 5 App |
1-30次测试,比较POSTman和Uniapp参数是否不同。比如检查file格式,相对还是绝对路径
30-50次测试,检查是否和内网相关联,因为我们公司是内网开发环境,后来发现并没有关系。
50次后,采用原生XmlHttpRequest写,依然无效。
80次后,采用uploader,我实在是精疲力竭,已经连续3天加班,每天从早测到晚,每次测试都毫无结果。
重新分析
所以我开始分析程序哪里出了了问题,再次判断分析为以下三个问题。
是否接口问题?用POSTMAN排除了。
是否语法问题?测了20次,派出了语法问题
是否方法问题?检查了前面两条路,(uni.uploade和xmlHttpRequest)我都发现死磕失败,最终走向第三条路——uploader
第100-130次:
fail to construct 'File:Iterator setter is not callable
这句话测试了30次,才发现是代码多了一个构造器new File
第130-180次
Uploader可以检测到上传过程中的数据问题,我检测了其中的数据,发现是已经上传了数据,但是结果却是失败的。
最终解决方案
我在180次测试结束后发现,多文件上传用uniapp多个路径始终走不通,得让后台配合把多文件接口改为单文件接口一对一传输才行。
这就是最终解决方案了。
收起阅读 »
输入框模糊搜索
插件预览图

使用教程
1.插件代码拷贝
- 下载后把components目录下inputSearch.vue文件拷贝到自己项目目录下
2.插件全局配置
- 在项目里main.js中配置如下代码
import inputSearch from './components/inputSearch.vue'
Vue.component('inputSearch', inputSearch)
3.插件使用
- vue页面使用
<template>
<view class="content">
<inputSearch :dataSource="dataSource" @select="handleChange" placeholder="请输入商品名称" />
</view>
</template>
<script>
export default {
data() {
return {
dataSource: [{
id: 1,
name: '耐克1'
},
{
id: 2,
name: '耐克2'
}
],
};
},
methods: {
//用户点击获取的数据
handleChange(data) {
console.log(data)
}
}
}
</script>
<style lang="scss">
.content {
padding:20upx;
}
</style>
兼容性
uni-app项目中使用都兼容
样式可以自己在组件里面调整
插件预览图
使用教程
1.插件代码拷贝
- 下载后把components目录下inputSearch.vue文件拷贝到自己项目目录下
2.插件全局配置
- 在项目里main.js中配置如下代码
import inputSearch from './components/inputSearch.vue'
Vue.component('inputSearch', inputSearch)
3.插件使用
- vue页面使用
<template>
<view class="content">
<inputSearch :dataSource="dataSource" @select="handleChange" placeholder="请输入商品名称" />
</view>
</template>
<script>
export default {
data() {
return {
dataSource: [{
id: 1,
name: '耐克1'
},
{
id: 2,
name: '耐克2'
}
],
};
},
methods: {
//用户点击获取的数据
handleChange(data) {
console.log(data)
}
}
}
</script>
<style lang="scss">
.content {
padding:20upx;
}
</style>
兼容性
uni-app项目中使用都兼容
样式可以自己在组件里面调整
收起阅读 »
An NoticeBar 通告栏 上下滚动 循环滚动通告栏 发布啦 插件市场
向上滚动通告栏 插件市场 下载地址 https://ext.dcloud.net.cn/plugin?id=1036
An NoticeBar 通告栏 上下滚动 循环滚动通告栏
向上滚动通告栏 插件市场 下载地址 https://ext.dcloud.net.cn/plugin?id=1036
An NoticeBar 通告栏 上下滚动 循环滚动通告栏

video组件,不定时的卡
内容是一些直播平台的m3u8,在全屏和不全屏播放的时候,有时候会进入缓存,然后,不自动缓存,必须重新退出在打开,不定时的出现这种情况。
内容是一些直播平台的m3u8,在全屏和不全屏播放的时候,有时候会进入缓存,然后,不自动缓存,必须重新退出在打开,不定时的出现这种情况。

动态判断权限
最近做unapp遇到手机授权问题,查看官网竟然什么都没写,只给一个插件市场的连接。引入到代码了,完美运行。过一段时间发现竟然部分手机不支持。一步步排查问题,发现竟然是官方提供的方法plus.android.requestPermissions有问题,部分手机调用这个授权方法一直显示已允许授权。后来多亏有高人指点,用原生插件解决问题,插件已上传附件中,使用方法,1项目中集成插件 2 代码中应用 var reqsdkwx = uni.requireNativePlugin('Yany-PermissionReqWX');reqsdkwx.checkPermissions(["android.permission.CAMERA","android.permission.RECORD_AUDIO"], function(res){});
最近做unapp遇到手机授权问题,查看官网竟然什么都没写,只给一个插件市场的连接。引入到代码了,完美运行。过一段时间发现竟然部分手机不支持。一步步排查问题,发现竟然是官方提供的方法plus.android.requestPermissions有问题,部分手机调用这个授权方法一直显示已允许授权。后来多亏有高人指点,用原生插件解决问题,插件已上传附件中,使用方法,1项目中集成插件 2 代码中应用 var reqsdkwx = uni.requireNativePlugin('Yany-PermissionReqWX');reqsdkwx.checkPermissions(["android.permission.CAMERA","android.permission.RECORD_AUDIO"], function(res){});
收起阅读 »
官方把这个集成到HBX里面。。。。。。
官方把这个集成到HBX里面。。。。。。 https://ext.dcloud.net.cn/plugin?id=1035
肯定是趋势,也是必然,现在敲代码太累了。。。
官方把这个集成到HBX里面。。。。。。 https://ext.dcloud.net.cn/plugin?id=1035
肯定是趋势,也是必然,现在敲代码太累了。。。

基于vuedraggable.js + uni 的可视化拖拽编程,自动生成项目,自动生成代码,自行导入第三方组件,纯前端

小米即时消息云IM,uni-APP 的sdk demo中 安卓端报错,网络断开问题解决
小米 免费 即时通讯IM 功能 ,官方没有提供 uniapp 或微信小程序的sdk。
下方有“开发者修改的支持微信小程序的JS库”,有大神开发者修改的支持uniapp的sdk,是用WebJS SDK改来的
之前在11.11号前 app 需要修改成 "usingComponents" : false, 打包APP后,测试是可以用的,
但由于dcloud官方 已不支持 非自定义组件模式 ,
目前打包app后,测试发现 安卓端不稳定,消息发不出去,无法登录,报错,掉线等问题。
- 使用此“小米即时消息云 MIMC uni-app SDK”,sdk开发的APP,在11月份以前"usingComponents" : false,是可以正常使用的,
但11号 uniapp 取消非自定义组件模式的编译,安卓端目前会报错,发消息不稳定,连接断开等问题。
废话不多说,解决办法: https://ext.dcloud.net.cn/plugin?id=647,
引用此js插件即可解决,。
main.js 中在引用 mimc框架前 下如下代码
import socket from '@/js_sdk/plus-websocket/index.js';
// #ifdef APP-PLUS
Object.assign(uni, socket)
// #endif
即可解决安卓端不稳定,报错等问题,
经测试收发消息已正常,无报错。
小米 免费 即时通讯IM 功能 ,官方没有提供 uniapp 或微信小程序的sdk。
下方有“开发者修改的支持微信小程序的JS库”,有大神开发者修改的支持uniapp的sdk,是用WebJS SDK改来的
之前在11.11号前 app 需要修改成 "usingComponents" : false, 打包APP后,测试是可以用的,
但由于dcloud官方 已不支持 非自定义组件模式 ,
目前打包app后,测试发现 安卓端不稳定,消息发不出去,无法登录,报错,掉线等问题。
- 使用此“小米即时消息云 MIMC uni-app SDK”,sdk开发的APP,在11月份以前"usingComponents" : false,是可以正常使用的,
但11号 uniapp 取消非自定义组件模式的编译,安卓端目前会报错,发消息不稳定,连接断开等问题。
废话不多说,解决办法: https://ext.dcloud.net.cn/plugin?id=647,
引用此js插件即可解决,。
main.js 中在引用 mimc框架前 下如下代码
import socket from '@/js_sdk/plus-websocket/index.js';
// #ifdef APP-PLUS
Object.assign(uni, socket)
// #endif
即可解决安卓端不稳定,报错等问题,
经测试收发消息已正常,无报错。

5+App使用UniPush发送消息,App在线、离线均能收到消息推送,并在通知栏进行提醒,苹果、华为、小米手机均测试通过
【1】本文档中使用的全是逶传消息,没有使用个推的其他消息
【2】需要开通UniPush功能,并在华为、小米开发者中添加App,并开通Push权限,目前不需要上架各厂商应用市场就可以使用,后续不知道需要不需要
【3】本实例使用Java后台开发,其他语言请自行翻译
【5】关于receive事件,只有发送的是透传数据【而且】不是标准格式【而且】当前应用在活动,这3个条件同时满足,才可以响应receive事件!!!,但是消息中心并没有消息展示!!!需要创建本地消息!!!才能在消息中心提醒,对于IOS的,一定要控制死循环!!!【重要】【重要】【重要】
只有APP在线时,才会触发receive事件,透传消息不会触发系统消息,需要创建本地消息【重要】【重要】【重要】
【6】关于click事件,click一定是点击通知栏的消息,才能触发!!! 下面两句话很重要
【APP在线】,收到透传消息通过,不会提醒至通知栏目,需要发送本地消息,再进行点击触发的点击事件。
【APP离线】,收到离线透传消息,必须通过Java后台的Intent字符串携带payload,且符合格式才能触发click事件,格式不符合不会触发。
【7】厂商推送需要设置的Intent字符串格式,请将${packageName},${title},${content},${payload}替换为正确的内容
${packageName} 代表应用包名
${title} 通知的标题
${content} 通知的内容
${payload} 其他附加参数,请用 JSON.toJSONString(payload)进行转码。
intent:#Intent;launchFlags=0x04000000;action=android.intent.action.oppopush;package=${packageName};component=${packageName}/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=${title};S.content=${content};S.payload=${payload};end
【8】APP端的javaScript代码,不需要必须写在第一个页面,我的项目写在了里面的页面,只要App启动后能打开这个页面就可以:
//收到透传消息
//只有APP在线时,才会触发receive事件,透传消息不会触发系统消息,需要创建本地消息
plus.push.addEventListener("receive", function(msg) {
console.log("(receive):" + JSON.stringify(msg));
if (app.isIOS()) { //如果是IOS
var payload = msg.payload;
//【APP离线】收到消息,但没有提醒(发生在一次收到多个离线消息时,只有一个有提醒,但其他的没有提醒)
//【APP在线】收到消息,不会触发系统消息,需要创建本地消息,但不能重复创建。必须加msg.type验证去除死循环
if (msg.aps == null && msg.type == "receive") {
var messageTitle = payload.messageTitle;
var messageContent = payload.messageContent;
//创建本地消息,发送的本地消息也会被receive方法接收到,但没有type属性,且aps是null
plus.push.createMessage(messageContent, JSON.stringify(payload), {title: messageTitle});
}
}
if (app.isAndroid()) { //如果是Android,当APP在线时,收到透传消息不会进入系统消息,需要发送本地提醒。
var payload = JSON.parse(msg.payload);
var messageTitle = payload.messageTitle;
var messageContent = payload.messageContent;
plus.push.createMessage(messageContent, msg.payload, {title: messageTitle});
}
}, false);
//消息点击事件
//【APP在线】,收到透传消息通过,不会提醒至通知栏目,需要发送本地消息,再进行点击触发的点击事件。
//【APP离线】,收到离线透传消息,必须通过Java后台的Intent字符串携带payload,且符合格式才能触发click事件,格式不符合不会触发。
plus.push.addEventListener("click", function(msg) {
console.log("(click):" + JSON.stringify(msg));
if (app.isIOS()) { //如果是IOS
var payload;
if (msg.type == "click") { //APP离线点击包含click属性,这时payload是JSON对象
payload = msg.payload;
} else { //APP在线,收到消息不会包含type属性,这时的payload是JSON字符串,需要转为JSON对象
payload = JSON.parse(msg.payload);
}
if (payload != null || payload != undefined) {
var messageType = payload.messageType;
messageClick(messageType, payload);
}
}
if (app.isAndroid()) { //如果是Android,收到playload均是是JSON字符串,需要转为JSON对象
var payload = JSON.parse(msg.payload);
if (payload != null || payload != undefined) {
var messageType = payload.messageType;
messageClick(messageType, payload);
}
}
}, false);
【9】java后台发送代码 ,代码中注释的内容,本人没有研究明白,未进行测试。
/**
* 推送消息至App所有用户,100次/天,每分钟不能超过5次,相同的消息内容,10分钟内不可以重复发送<br/>
* 华为,平果测试通过
*
* @param getuiMessage
* @return
*/
public static IPushResult toApp(GetuiMessage getuiMessage) {
TransmissionTemplate template = PushTemplate.getTransmissionTemplateWith3rdNotifyInfoAndAPNPayload(getuiMessage.getMessageTitle(),
getuiMessage.getMessageContent(), "+1", getuiMessage);
AppMessage message = new AppMessage();
message.setData(template);
message.setOffline(true);
message.setOfflineExpireTime(24 * 1000 * 3600);
message.setAppIdList(Arrays.asList(GetuiConfig.me.getAppId()));
IPushResult ret = GetuiConfig.me.getPush().pushMessageToApp(message);
GetuiConfig.logger.info(GetuiConfig.logger.getName() + JSON.toJSONString(ret));
return ret;
}
/**
* 推送消息至特定的ClientID,频次限制:没有限制!
*
* @param getuiMessage 消息对象
* @param clientId 接收的ClientId
* @return
*/
public static IPushResult toSingle(GetuiMessage getuiMessage, String clientId) {
// 获取消息模板
TransmissionTemplate template = PushTemplate.getTransmissionTemplateWith3rdNotifyInfoAndAPNPayload(getuiMessage.getMessageTitle(),
getuiMessage.getMessageContent(), "+1", getuiMessage);
// 制作消息
SingleMessage message = new SingleMessage();
message.setData(template);
message.setOffline(true);// 设置消息离线,并设置离线时间
message.setOfflineExpireTime(72 * 3600 * 1000); // 离线有效时间,单位为毫秒,可选
// message.setPriority(1);// 优先级
// message.setPushNetWorkType(0); // 判断客户端是否wifi环境下推送。1为仅在wifi环境下推送,0为不限制网络环境,默认不限
// 生成接收人
Target target = new Target();
target.setAppId(GetuiConfig.me.getAppId());
target.setClientId(clientId);
// 发送消息
IPushResult ret = GetuiConfig.me.getPush().pushMessageToSingle(message, target);
GetuiConfig.logger.info(GetuiConfig.logger.getName() + JSON.toJSONString(ret));
return ret;
}
/**
* 获取同时有Android第三方推送及IOS推送功能的很透传消息
*
* @param title 标题
* @param body 正文
* @param badge IOS的角标数
* @param customParam 自定义属性
* @return
*/
public static TransmissionTemplate getTransmissionTemplateWith3rdNotifyInfoAndAPNPayload(String title, String body, String badge,
Map<String, String> customParam) {
TransmissionTemplate template = new TransmissionTemplate();
// 设置APPID与APPKEY
template.setAppId(GetuiConfig.me.getAppId());
template.setAppkey(GetuiConfig.me.getAppKey());
// 透传消息设置,1为强制启动应用,客户端接收到消息后就会立即启动应用;2为等待应用启动
template.setTransmissionType(2);
template.setTransmissionContent(JSON.toJSONString(customParam)); // 透传内容
// 第三方厂商推送
template.set3rdNotifyInfo(get3rdNotifyInfo(title, body, customParam));
// 针对IOS,设置APNs
template.setAPNInfo(getAPNPayload(title, body, badge, customParam)); // ios消息推送
return template;
}
/**
* 第三方厂商通知
*
* @param title 标题
* @param content 正文
* @param payload 附带属性
* @return
*/
private static Notify get3rdNotifyInfo(String title, String content, Map<String, String> payload) {
Notify notify = new Notify();
notify.setTitle(title);
notify.setContent(content);
notify.setType(Type._intent);
notify.setIntent(GetuiConfig.me.getIntent(title,content,payload));
notify.setPayload(JSON.toJSONString(payload));
return notify;
}
/**
* IOS的APNs消息
*
* @param title
* @param body
* @param badge
* @param customMsg
* @return
*/
private static APNPayload getAPNPayload(String title, String body, String badge, Map<String, String> customMsg) {
APNPayload payload = new APNPayload();
// 在已有数字基础上加1显示,设置为-1时,在已有数字上减1显示,设置为数字时,显示指定数字
if (badge != null && badge.trim().length() > 0) {
payload.setAutoBadge(badge);
}
payload.setContentAvailable(1);
// ios 12.0 以上可以使用 Dictionary 类型的 sound
payload.setSound("default");
// payload.setCategory("$由客户端定义");
if (customMsg != null) {
for (Entry<String, String> enty : customMsg.entrySet()) {
payload.addCustomMsg(enty.getKey(), enty.getValue());
}
}
// payload.setAlertMsg(new APNPayload.SimpleAlertMsg("helloCCCC"));//简单模式APNPayload.SimpleMsg
payload.setAlertMsg(getDictionaryAlertMsg(title, body)); // 字典模式使用APNPayload.DictionaryAlertMsg
// // 设置语音播报类型,int类型,0.不可用 1.播放body 2.播放自定义文本
// payload.setVoicePlayType(2);
// // 设置语音播报内容,String类型,非必须参数,用户自定义播放内容,仅在voicePlayMessage=2时生效
// // 注:当"定义类型"=2, "定义内容"为空时则忽略不播放
// payload.setVoicePlayMessage("定义内容");
//
// // 添加多媒体资源
// payload.addMultiMedia(new MultiMedia().setResType(MultiMedia.MediaType.pic).setResUrl("资源文件地址").setOnlyWifi(true));
return payload;
}
/**
* IOS通知提示样式
*
* @param title
* @param body
* @return
*/
private static APNPayload.DictionaryAlertMsg getDictionaryAlertMsg(String title, String body) {
APNPayload.DictionaryAlertMsg alertMsg = new APNPayload.DictionaryAlertMsg();
alertMsg.setBody(body);
// alertMsg.setActionLocKey("显示关闭和查看两个按钮的消息");
// alertMsg.setLocKey("loc-key1");
// alertMsg.addLocArg("loc-ary1");
// alertMsg.setLaunchImage("调用已经在应用程序中绑定的图形文件名");
// iOS8.2以上版本支持
alertMsg.setTitle(title);
// alertMsg.setTitleLocKey("自定义通知标题");
// alertMsg.addTitleLocArg("自定义通知标题组");
return alertMsg;
}
/**
* 需要使用iOS语音传输,请使用VoIPPayload代替APNPayload【未测试】
*
*
* @return
*/
private static VoIPPayload getVoIPPayload() {
//TODO 未测试,未开发完成
VoIPPayload payload = new VoIPPayload();
JSONObject jo = new JSONObject();
jo.put("key1", "value1");
payload.setVoIPPayload(jo.toString());
return payload;
}
public class GetuiMessage extends HashMap<String, String> {
/**
*
*/
private static final long serialVersionUID = -5993986890576211345L;
private final String messageTitle = "messageTitle";
private final String messageContent = "messageContent";
private final String messageType = "messageType";
/**
* 生成推送消息
*
* @param typeEnum 消息类型
* @param customParam 消息内容参数和消息点击需要的参加共享相同参数
*/
public GetuiMessage(MessageType typeEnum, Map<String, String> customParam) {
if (customParam != null && customParam.size() > 0) {
this.putAll(customParam);
}
// 把优先级高的Key放在后面,即遍在customParam有重复的KEY,也不会影响。
this.put(messageTitle, typeEnum.getTitle());
this.put(messageType, typeEnum.getCode());
this.put(messageContent, typeEnum.getContent(customParam));
}
参考(Push推送使用指南):https://ask.dcloud.net.cn/article/35622
【1】本文档中使用的全是逶传消息,没有使用个推的其他消息
【2】需要开通UniPush功能,并在华为、小米开发者中添加App,并开通Push权限,目前不需要上架各厂商应用市场就可以使用,后续不知道需要不需要
【3】本实例使用Java后台开发,其他语言请自行翻译
【5】关于receive事件,只有发送的是透传数据【而且】不是标准格式【而且】当前应用在活动,这3个条件同时满足,才可以响应receive事件!!!,但是消息中心并没有消息展示!!!需要创建本地消息!!!才能在消息中心提醒,对于IOS的,一定要控制死循环!!!【重要】【重要】【重要】
只有APP在线时,才会触发receive事件,透传消息不会触发系统消息,需要创建本地消息【重要】【重要】【重要】
【6】关于click事件,click一定是点击通知栏的消息,才能触发!!! 下面两句话很重要
【APP在线】,收到透传消息通过,不会提醒至通知栏目,需要发送本地消息,再进行点击触发的点击事件。
【APP离线】,收到离线透传消息,必须通过Java后台的Intent字符串携带payload,且符合格式才能触发click事件,格式不符合不会触发。
【7】厂商推送需要设置的Intent字符串格式,请将${packageName},${title},${content},${payload}替换为正确的内容
${packageName} 代表应用包名
${title} 通知的标题
${content} 通知的内容
${payload} 其他附加参数,请用 JSON.toJSONString(payload)进行转码。
intent:#Intent;launchFlags=0x04000000;action=android.intent.action.oppopush;package=${packageName};component=${packageName}/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=${title};S.content=${content};S.payload=${payload};end
【8】APP端的javaScript代码,不需要必须写在第一个页面,我的项目写在了里面的页面,只要App启动后能打开这个页面就可以:
//收到透传消息
//只有APP在线时,才会触发receive事件,透传消息不会触发系统消息,需要创建本地消息
plus.push.addEventListener("receive", function(msg) {
console.log("(receive):" + JSON.stringify(msg));
if (app.isIOS()) { //如果是IOS
var payload = msg.payload;
//【APP离线】收到消息,但没有提醒(发生在一次收到多个离线消息时,只有一个有提醒,但其他的没有提醒)
//【APP在线】收到消息,不会触发系统消息,需要创建本地消息,但不能重复创建。必须加msg.type验证去除死循环
if (msg.aps == null && msg.type == "receive") {
var messageTitle = payload.messageTitle;
var messageContent = payload.messageContent;
//创建本地消息,发送的本地消息也会被receive方法接收到,但没有type属性,且aps是null
plus.push.createMessage(messageContent, JSON.stringify(payload), {title: messageTitle});
}
}
if (app.isAndroid()) { //如果是Android,当APP在线时,收到透传消息不会进入系统消息,需要发送本地提醒。
var payload = JSON.parse(msg.payload);
var messageTitle = payload.messageTitle;
var messageContent = payload.messageContent;
plus.push.createMessage(messageContent, msg.payload, {title: messageTitle});
}
}, false);
//消息点击事件
//【APP在线】,收到透传消息通过,不会提醒至通知栏目,需要发送本地消息,再进行点击触发的点击事件。
//【APP离线】,收到离线透传消息,必须通过Java后台的Intent字符串携带payload,且符合格式才能触发click事件,格式不符合不会触发。
plus.push.addEventListener("click", function(msg) {
console.log("(click):" + JSON.stringify(msg));
if (app.isIOS()) { //如果是IOS
var payload;
if (msg.type == "click") { //APP离线点击包含click属性,这时payload是JSON对象
payload = msg.payload;
} else { //APP在线,收到消息不会包含type属性,这时的payload是JSON字符串,需要转为JSON对象
payload = JSON.parse(msg.payload);
}
if (payload != null || payload != undefined) {
var messageType = payload.messageType;
messageClick(messageType, payload);
}
}
if (app.isAndroid()) { //如果是Android,收到playload均是是JSON字符串,需要转为JSON对象
var payload = JSON.parse(msg.payload);
if (payload != null || payload != undefined) {
var messageType = payload.messageType;
messageClick(messageType, payload);
}
}
}, false);
【9】java后台发送代码 ,代码中注释的内容,本人没有研究明白,未进行测试。
/**
* 推送消息至App所有用户,100次/天,每分钟不能超过5次,相同的消息内容,10分钟内不可以重复发送<br/>
* 华为,平果测试通过
*
* @param getuiMessage
* @return
*/
public static IPushResult toApp(GetuiMessage getuiMessage) {
TransmissionTemplate template = PushTemplate.getTransmissionTemplateWith3rdNotifyInfoAndAPNPayload(getuiMessage.getMessageTitle(),
getuiMessage.getMessageContent(), "+1", getuiMessage);
AppMessage message = new AppMessage();
message.setData(template);
message.setOffline(true);
message.setOfflineExpireTime(24 * 1000 * 3600);
message.setAppIdList(Arrays.asList(GetuiConfig.me.getAppId()));
IPushResult ret = GetuiConfig.me.getPush().pushMessageToApp(message);
GetuiConfig.logger.info(GetuiConfig.logger.getName() + JSON.toJSONString(ret));
return ret;
}
/**
* 推送消息至特定的ClientID,频次限制:没有限制!
*
* @param getuiMessage 消息对象
* @param clientId 接收的ClientId
* @return
*/
public static IPushResult toSingle(GetuiMessage getuiMessage, String clientId) {
// 获取消息模板
TransmissionTemplate template = PushTemplate.getTransmissionTemplateWith3rdNotifyInfoAndAPNPayload(getuiMessage.getMessageTitle(),
getuiMessage.getMessageContent(), "+1", getuiMessage);
// 制作消息
SingleMessage message = new SingleMessage();
message.setData(template);
message.setOffline(true);// 设置消息离线,并设置离线时间
message.setOfflineExpireTime(72 * 3600 * 1000); // 离线有效时间,单位为毫秒,可选
// message.setPriority(1);// 优先级
// message.setPushNetWorkType(0); // 判断客户端是否wifi环境下推送。1为仅在wifi环境下推送,0为不限制网络环境,默认不限
// 生成接收人
Target target = new Target();
target.setAppId(GetuiConfig.me.getAppId());
target.setClientId(clientId);
// 发送消息
IPushResult ret = GetuiConfig.me.getPush().pushMessageToSingle(message, target);
GetuiConfig.logger.info(GetuiConfig.logger.getName() + JSON.toJSONString(ret));
return ret;
}
/**
* 获取同时有Android第三方推送及IOS推送功能的很透传消息
*
* @param title 标题
* @param body 正文
* @param badge IOS的角标数
* @param customParam 自定义属性
* @return
*/
public static TransmissionTemplate getTransmissionTemplateWith3rdNotifyInfoAndAPNPayload(String title, String body, String badge,
Map<String, String> customParam) {
TransmissionTemplate template = new TransmissionTemplate();
// 设置APPID与APPKEY
template.setAppId(GetuiConfig.me.getAppId());
template.setAppkey(GetuiConfig.me.getAppKey());
// 透传消息设置,1为强制启动应用,客户端接收到消息后就会立即启动应用;2为等待应用启动
template.setTransmissionType(2);
template.setTransmissionContent(JSON.toJSONString(customParam)); // 透传内容
// 第三方厂商推送
template.set3rdNotifyInfo(get3rdNotifyInfo(title, body, customParam));
// 针对IOS,设置APNs
template.setAPNInfo(getAPNPayload(title, body, badge, customParam)); // ios消息推送
return template;
}
/**
* 第三方厂商通知
*
* @param title 标题
* @param content 正文
* @param payload 附带属性
* @return
*/
private static Notify get3rdNotifyInfo(String title, String content, Map<String, String> payload) {
Notify notify = new Notify();
notify.setTitle(title);
notify.setContent(content);
notify.setType(Type._intent);
notify.setIntent(GetuiConfig.me.getIntent(title,content,payload));
notify.setPayload(JSON.toJSONString(payload));
return notify;
}
/**
* IOS的APNs消息
*
* @param title
* @param body
* @param badge
* @param customMsg
* @return
*/
private static APNPayload getAPNPayload(String title, String body, String badge, Map<String, String> customMsg) {
APNPayload payload = new APNPayload();
// 在已有数字基础上加1显示,设置为-1时,在已有数字上减1显示,设置为数字时,显示指定数字
if (badge != null && badge.trim().length() > 0) {
payload.setAutoBadge(badge);
}
payload.setContentAvailable(1);
// ios 12.0 以上可以使用 Dictionary 类型的 sound
payload.setSound("default");
// payload.setCategory("$由客户端定义");
if (customMsg != null) {
for (Entry<String, String> enty : customMsg.entrySet()) {
payload.addCustomMsg(enty.getKey(), enty.getValue());
}
}
// payload.setAlertMsg(new APNPayload.SimpleAlertMsg("helloCCCC"));//简单模式APNPayload.SimpleMsg
payload.setAlertMsg(getDictionaryAlertMsg(title, body)); // 字典模式使用APNPayload.DictionaryAlertMsg
// // 设置语音播报类型,int类型,0.不可用 1.播放body 2.播放自定义文本
// payload.setVoicePlayType(2);
// // 设置语音播报内容,String类型,非必须参数,用户自定义播放内容,仅在voicePlayMessage=2时生效
// // 注:当"定义类型"=2, "定义内容"为空时则忽略不播放
// payload.setVoicePlayMessage("定义内容");
//
// // 添加多媒体资源
// payload.addMultiMedia(new MultiMedia().setResType(MultiMedia.MediaType.pic).setResUrl("资源文件地址").setOnlyWifi(true));
return payload;
}
/**
* IOS通知提示样式
*
* @param title
* @param body
* @return
*/
private static APNPayload.DictionaryAlertMsg getDictionaryAlertMsg(String title, String body) {
APNPayload.DictionaryAlertMsg alertMsg = new APNPayload.DictionaryAlertMsg();
alertMsg.setBody(body);
// alertMsg.setActionLocKey("显示关闭和查看两个按钮的消息");
// alertMsg.setLocKey("loc-key1");
// alertMsg.addLocArg("loc-ary1");
// alertMsg.setLaunchImage("调用已经在应用程序中绑定的图形文件名");
// iOS8.2以上版本支持
alertMsg.setTitle(title);
// alertMsg.setTitleLocKey("自定义通知标题");
// alertMsg.addTitleLocArg("自定义通知标题组");
return alertMsg;
}
/**
* 需要使用iOS语音传输,请使用VoIPPayload代替APNPayload【未测试】
*
*
* @return
*/
private static VoIPPayload getVoIPPayload() {
//TODO 未测试,未开发完成
VoIPPayload payload = new VoIPPayload();
JSONObject jo = new JSONObject();
jo.put("key1", "value1");
payload.setVoIPPayload(jo.toString());
return payload;
}
public class GetuiMessage extends HashMap<String, String> {
/**
*
*/
private static final long serialVersionUID = -5993986890576211345L;
private final String messageTitle = "messageTitle";
private final String messageContent = "messageContent";
private final String messageType = "messageType";
/**
* 生成推送消息
*
* @param typeEnum 消息类型
* @param customParam 消息内容参数和消息点击需要的参加共享相同参数
*/
public GetuiMessage(MessageType typeEnum, Map<String, String> customParam) {
if (customParam != null && customParam.size() > 0) {
this.putAll(customParam);
}
// 把优先级高的Key放在后面,即遍在customParam有重复的KEY,也不会影响。
this.put(messageTitle, typeEnum.getTitle());
this.put(messageType, typeEnum.getCode());
this.put(messageContent, typeEnum.getContent(customParam));
}
参考(Push推送使用指南):https://ask.dcloud.net.cn/article/35622
收起阅读 »
找人用Uni-app开发小程序及APP
我现在有个小程序需要用uin-app开发,后端及接口都已经写好,后端是JAVA 有时间做的联系我Q Q3013321536
我现在有个小程序需要用uin-app开发,后端及接口都已经写好,后端是JAVA 有时间做的联系我Q Q3013321536

h5+开发的app内容秒加载的方法
在由列表页进入详情页时,如果在打开详情页再去请求详情数据的话,除非你服务器配置带宽喜人,不然一般都会有一点延迟加载的感觉
怎样才能打开就秒开呢?其实做法就是提前缓存数据
比如,在打开app时有个启动动画,在加载启动动画页时就缓存需要的数据,比如文章标题、内容
原来我想通过mui预加载的形式看能不能先运行缓存页,结果证明是行不通的
没办法,只能用ajax先运行缓存页,由缓存页把数据先缓存好
比如定义hc.html 为缓存页面,那么在hc.html这个页面就去请求你服务器的数据,载入进来后存入客户端本地
这样从启动页进入首页后,你需要的数据也都载入进来了,一进页面就用缓存数据替换dom内容,而且这个数据都是最新的
当然你也可以在页面上拉新,如果是新数据就替换原来的缓存即可
在由列表页进入详情页时,如果在打开详情页再去请求详情数据的话,除非你服务器配置带宽喜人,不然一般都会有一点延迟加载的感觉
怎样才能打开就秒开呢?其实做法就是提前缓存数据
比如,在打开app时有个启动动画,在加载启动动画页时就缓存需要的数据,比如文章标题、内容
原来我想通过mui预加载的形式看能不能先运行缓存页,结果证明是行不通的
没办法,只能用ajax先运行缓存页,由缓存页把数据先缓存好
比如定义hc.html 为缓存页面,那么在hc.html这个页面就去请求你服务器的数据,载入进来后存入客户端本地
这样从启动页进入首页后,你需要的数据也都载入进来了,一进页面就用缓存数据替换dom内容,而且这个数据都是最新的
当然你也可以在页面上拉新,如果是新数据就替换原来的缓存即可