微信上传图片出现的问题和总结

先说一下微信上传图片(这里讲的是新增临时素材)
在微信开发者文档中的被动回复用户消息的开头就说明了:

请注意,回复图片等多媒体消息时需要预先上传多媒体文件到微信服务器,只支持认证服务号。

多媒体消息有图片、语音、视频,也可以这样想:只有回复消息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的写入流outout.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,0,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);
            //将请求消息体的格式尾写入outout.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,0,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);

Image类中只有一个属性media_id,有图有真相

public class Image {
    public String MediaId;
    //setter和getter
}

ImageRespMessage代码如下:

public class ImageRespMessage extends BaseRespMessage {
    private Image image;
    //setter和getter
}

然后就运行嘛,试几次都是“该公众号暂时无法提供服务,请稍后再试”这样的系统提示,我就纳闷了,怎么是我文件没上传成功,在”使用网页调试工具调试该接口”中测试了一下,图片妥妥的传给了微信服务器,证明access_token和media_id是有效的,那为什么还有系统提示呢?

请开发者注意,一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:

1、开发者在5秒内未回复任何内容
2、开发者回复了异常数据,比如JSON数据等

好的,微信服务器给出了答案。
首先我的服务器是不是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>

唯一的区别就是节点是首字母是大写的,是小写的,小写的不符合微信服务器的规范,所以就被列为异常消息了。

所以在封装消息时,一定要注意属性的名称是否和微信规范一样,最好的就是直接从微信开发者文档上复制。(话说我也发现微信开发者文档上有错别字,我也不知道对不对了,大家看看,“纬度”的“纬”在文档中打成了“维度”)。

出现错误时,把回复的xml数据打印出来和文档上的仔细对比,一个字符一个字符的对比,实在不行,写个程序equals一把。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

归否

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值