先说一下微信上传图片(这里讲的是新增临时素材)
在微信开发者文档中的被动回复用户消息的开头就说明了:
多媒体消息有图片、语音、视频,也可以这样想:只有回复消息xml格式中存在media_id的都必须先上传到微信服务器,得到微信服务器返回的media_id,然后再用这个media_id来调用多媒体。
关于怎么上传多媒体文件,可以参考下面3个材料:
微信开发者文档中的新增临时素材
微信公众号上传下载网络路径下的多媒体文件-java
微信公众号上传本地路径下的多媒体文件
我将本地路径和网络路径多媒体文件结合起来了,代码如下:
public class MediaUtil {
/** * 上传多媒体文件 * @param access_token 接口访问凭证 * @param mediaType 媒体文件类型,分别有图片(image)、语音(voice)、视频(video) * @param mediaUrl * 媒体文件的url ,本地路径和网络路径的文件都能上传 * * 图片大小不超过2M,支持bmp/png/jpeg/jpg/gif格式, * 语音大小不超过5M,长度不超过60秒,支持mp3/wma/wav/amr格式 * @return */
public static WeiXinMedia uploadMedia(String access_token,String mediaType,String mediaUrl) {
//返回数据
String result = "";
// 拼接上传地址uploadUrl
String uploadUrl = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE";
uploadUrl = uploadUrl.replace("ACCESS_TOKEN",access_token).replace(
"TYPE",mediaType);
try {
// 创建一个https连接
URL requstUrl = new URL(uploadUrl);
HttpsURLConnection conn = (HttpsURLConnection) requstUrl.openConnection();
conn.setRequestMethod("POST");//设置请求方式
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
//设置请求消息头
conn.setRequestProperty("Connection","Keep-Alive");
conn.setRequestProperty("Charset","UTF-8");
String boundary = "----" + System.currentTimeMillis();//分隔字符串
conn.setRequestProperty("Content-Type","multipart/form-data;boundary=" + boundary);
//拼接请求消息体的格式头
StringBuilder sb1 = new StringBuilder();
sb1.append("--");
sb1.append(boundary);
sb1.append("\r\n"); //回车
sb1.append("Content-Disposition:form-data;name=\"file\";filename=\""+mediaUrl+"\"\r\n");//回车
sb1.append("Content-Type:application/octet-stream\r\n");//回车
sb1.append("\r\n");//回车 - 空行
byte[] head = sb1.toString().getBytes("UTF-8");
//获得连接的写入流out
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
//将请求消息体的格式头写入conn的写入流out中
out.write(head);
//读数据要用的
int len = 0;
byte[] buf = new byte[1024];
//>>>>>>>>>>判断mediaUrl是本地路径还是网络路径
if(mediaUrl.startsWith("http")){
URL url2 = new URL(mediaUrl);
HttpURLConnection conn2 = (HttpURLConnection) url2.openConnection();
conn2.setDoInput(true);
conn2.setRequestMethod("GET");
BufferedInputStream bis = new BufferedInputStream(conn2.getInputStream());
while((len = bis.read(buf))>0){
out.write(buf,0,len);
}
bis.close();
conn2.disconnect();
}else{
//将请求消息体的正文写入conn的写入流out中
DataInputStream in = new DataInputStream(new FileInputStream(mediaUrl));
while((len = in.read(buf))>0){
out.write(buf,len);
}
in.close();//关闭流
}
System.out.println("文件写入完成 ...");
//拼接请求消息体的格式尾
StringBuilder sb2 = new StringBuilder();
sb2.append("\r\n");//回车 - 请求消息体的正文和格式头、格式尾分别隔着一个空行
sb2.append("--");
sb2.append(boundary);
sb2.append("--");
sb2.append("\r\n");//回车
byte[] foot = sb2.toString().getBytes("UTF-8");
//System.out.println(foot);
//将请求消息体的格式尾写入out流
out.write(foot);
out.flush();
out.close();
System.out.println("上传多媒体请求消息发出 ...");
//读取服务器响应的消息
StringBuffer temp = new StringBuffer();
InputStream respIn = conn.getInputStream();
len = 0;
while((len = respIn.read(buf))>0){
temp.append(new String(buf,len));
}
respIn.close();
conn.disconnect();
result = temp.toString();
} catch (IOException e) {
System.out.println("发送POST请求出现异常!" + e);
e.printStackTrace();
}
System.out.println("微信服务器回复的消息:" + result);
JSONObject json = JSONObject.fromObject(result);
//组装WeiXinMedia
WeiXinMedia wxMedia = new WeiXinMedia();
wxMedia.setType(json.getString("type"));
wxMedia.setMedia_id(json.getString("media_id"));
wxMedia.setCreated_at(json.getInt("created_at"));
return wxMedia;
}
}
WeiXinMedia的代码如下:
public class WeiXinMedia {
private String type;
private String media_id;
private int created_at;
//省略setter和getter
如果对其中的上传文件的http消息格式不明白,可以参考:
理解Http消息头 - 看 4 Post 小节
说了半天,故事才开始
有了MediaUtil那我还不大干一场,在CoreService类中
Image image = new Image();
image.setMediaId(MediaUtil.uploadMedia(access_token,"image","c:/1.png").getMedia_id());
ImageRespMessage imageRespMessage = new ImageRespMessage();
imageRespMessage.setToUserName(fromUserName);
imageRespMessage.setFromUserName(toUserName);
imageRespMessage.setCreateTime(new Date().getTime());
imageRespMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_IMAGE);
imageRespMessage.setImage(image);
respXml = MessageUtil.respMessage2Xml(imageRespMessage);
public class Image {
public String MediaId;
//setter和getter
}
ImageRespMessage代码如下:
public class ImageRespMessage extends BaseRespMessage {
private Image image;
//setter和getter
}
然后就运行嘛,试几次都是“该公众号暂时无法提供服务,请稍后再试”这样的系统提示,我就纳闷了,怎么是我文件没上传成功,在”使用网页调试工具调试该接口”中测试了一下,图片妥妥的传给了微信服务器,证明access_token和media_id是有效的,那为什么还有系统提示呢?
好的,微信服务器给出了答案。
首先我的服务器是不是5秒内没有回复呢?没有,绝对没有没回复。因为回复的xml数据好好的打印了出来。如下:
<xml>
<ToUserName><![CDATA[oBIBht8GBKmMk5os5xF4U_SvmhdY]]></ToUserName>
<FromUserName><![CDATA[gh_48efdea48052]]></FromUserName>
<CreateTime><![CDATA[1429841278925]]></CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<image>
<MediaId><![CDATA[F-KFiCTQIzfdAWlChSIhaVCbWbVq3oTy-NHeIq7G0vk5BYDU_P4JFbf2drv0eNSs]]></MediaId>
</image>
</xml>
那就是回复了数据异常的事了?
对照着微信开发者文档瞅了好几遍,终于被我发现了。都瞅瞅!下面的xml格式和上面的有什么不同?
<xml>
<ToUserName><![CDATA[oBIBht8GBKmMk5os5xF4U_SvmhdY]]></ToUserName>
<FromUserName><![CDATA[gh_48efdea48052]]></FromUserName>
<CreateTime><![CDATA[1429841278925]]></CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[F-KFiCTQIzfdAWlChSIhaVCbWbVq3oTy-NHeIq7G0vk5BYDU_P4JFbf2drv0eNSs]]></MediaId>
</Image>
</xml>
唯一的区别就是节点是首字母是大写的,是小写的,小写的不符合微信服务器的规范,所以就被列为异常消息了。
所以在封装消息时,一定要注意属性的名称是否和微信规范一样,最好的就是直接从微信开发者文档上复制。(话说我也发现微信开发者文档上有错别字,我也不知道对不对了,大家看看,“纬度”的“纬”在文档中打成了“维度”)。