没有服务器如何进行微信开发

2018-10-22 09:59:36 weixin_41695965 阅读数 7814

微信开发 — 调用微信上传图片接口,并保存到自己的服务器

整体思路是这样的:
1.先把手机上的图片上传到微信服务器,然后返回一个图片ID
2.在通过后台根据ID从微信后台拿到流,保存到服务器
前几个步骤在之前的博客上有提到调通wx.config{}
js

$.ajax({
		type : "post",
		url : "wx/sys.do", //之前的博客上有写到
		data : {
			"url" : location.href.split('#')[0]
		},
		dataType : "json",
		success : function(data) {
			wx.config({
				debug : false, // 开启调试模式   
				appId : data.data.appId,
				timestamp : data.data.timestamp,
				nonceStr : data.data.nonceStr,
				signature : data.data.signature,
				jsApiList : [ 'checkJsApi', 'uploadImage', 'chooseImage' ]
			// 必填,需要使用的JS接口列表,所有JS接口列表见附录2 
			});
			wx.error(function(res) {
				alert("出错了:" + res.errMsg);//这个地方的好处就是wx.config配置错误,会弹出窗口哪里错误,然后根据微信文档查询即可。
			});

			wx.ready(function() {
				wx.checkJsApi({
					jsApiList : [ 'chooseImage' ],
					success : function(res) {

					}
				});
			});
			//点击按钮
			$("#scanQRCode").click(function() {
				wx.chooseImage({
					count : 1, // 默认9
					sizeType : [ 'original', 'compressed' ], // 可以指定是原图还是压缩图,默认二者都有
					sourceType : [ 'album', 'camera' ], // 可以指定来源是相册还是相机,默认二者都有
					success : function(res) {
						var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
						localIdArr = localIds;
						uploadImg(localIds);
					}
				});
			});

		}
	});

	var int = 0;
	var serverIdArr = new Array();
	var localIdArr = new Array();
	function uploadImg(localIds) {//上传成功,微信服务器会返回一个本地ID,可以预览
		wx.uploadImage({//根据本地ID获得微信服务器ID
			localId : localIds[int].toString(), // 需要上传的图片的本地ID,由chooseImage接口获得
			isShowProgressTips : 1, // 默认为1,显示进度提示
			success : function(res) {
				var serverId = res.serverId; // 返回图片的服务器端ID
				var id = $('#id').val();//id
				$.ajax({
					type : "post",
					//这个方法很重要
					url : "wx/upload.do?mediaId=" + serverId + "&id=" + id,//mediaId这个就是微信返回的id传到后台
					data : {
					//data: {"mediaId": serverId},  
					},
					dataType : "json",
					success : function(msg) {
						if (msg.code == 1) {
							top.layer.msg("上传成功!", {
								icon : 6
							});
							location.reload();
						} else {
							lock = true;
							top.layer.msg(msg.msg, {
								icon : 5
							});
						}
					}
				});

			}
		});
	}

jsp
因为框架不同 样式可能不太一样
我这个上传成功后刷新页面从服务器取一下上传的图片

<div class="layui-input-block" style="margin-top: 10px; margin-bottom: 10px; display: block;" id="divy">
			<img <c:if test="${url.url == null}">src="images/null.jpg" </c:if> src="/${url.url}" width="160px" height="150px" />
</div>
<div class="layui-input-block" style="margin-top: 10px; margin-bottom: 10px;">
			<button class="layui-btn layui-btn-normal" id="scanQRCode" style="text-align: center; background-color: #ff4e00; width: 160px;">上传微信二维码</button>
</div> 

Controller

/**
	 * @Method 微信图片下载到服务器
	 * @author 
	 * @throws Exception
	 * @createTime
	 */
	@ResponseBody
	@RequestMapping("upload")
	public JsonBean upload(String mediaId, HttpServletRequest servletRequest, HttpServletRequest request, int id) throws Exception {
		InputStream input = null;
		FileOutputStream output = null;
		try {
		// 从微信获取的流,这个utils就是根据返回mediaId后去流的
			input = picDownload.getInputStream(mediaId);
			String path = "qr";
			File folder = new File(uploadFilePath + path);
			if (!folder.exists()) {
				folder.mkdirs();
			}

			File targetFile = new File(folder, new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + ".jpg");
			output = new FileOutputStream(targetFile);
			IOUtils.copy(input, output);
            //上边就是把图片保存到服务器里
            //下边是数据库的一些操作
			// 如果数据库有就删除
			QrMe qrMe = qrMeService.selectTeacherId(id);
			if (qrMe != null) {
				qrMeService.deleteUrl(id);
				new File(uploadFilePath + qrMe.getUrl()).delete();
			}

			// 添加数据库
			QrMe newQrMe = new QrMe();
			newQrMe.setUrl(path + "/" + targetFile.getName());
			newQrMe.setTeacherid(id);
			return qrMeService.insertQR(newQrMe);
		} finally {
			IOUtils.closeQuietly(input);
			IOUtils.closeQuietly(output);
		}
	}

utils

package com.admin.commons.utils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class PicDownload {

   //微信接口 
	@Autowired
	private WeiXinUtils weiXinUtils;
	
	/**
	 * 根据文件id下载文件
	 * 
	 * @param mediaId 媒体id
	 * @throws IOException
	 * @throws Exception
	 */
	public InputStream getInputStream(String mediaId) throws IOException {
		InputStream is = null;
		String url = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=" + weiXinUtils.getAccessToken() + "&media_id=" + mediaId;  根据AccessToken获取media

		try {
			URL urlGet = new URL(url);
			HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
			http.setRequestMethod("GET"); // 必须是get方式请求
			http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			http.setDoOutput(true);
			http.setDoInput(true);
			System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
			System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
			http.connect();
			is = http.getInputStream();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return is;
	}

	/**
	 * 获取下载图片信息(jpg)
	 * 
	 * @param mediaId 文件的id
	 * @throws Exception
	 */

	public void saveImageToDisk(String mediaId) throws Exception {
		InputStream inputStream = getInputStream(mediaId);
		byte[] data = new byte[1024];
		int len = 0;
		FileOutputStream fileOutputStream = null;
		try {
			fileOutputStream = new FileOutputStream("test1.jpg");
			while ((len = inputStream.read(data)) != -1) {
				fileOutputStream.write(data, 0, len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			if (fileOutputStream != null) {
				try {
					fileOutputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

刚开始接触这个功能,在网上找了很多资料踩了很多坑,图片不是直接上传到服务器的,而是先上传到微信的服务器,然后再下载到自己的服务器
前台我用的是layui框架

2017-10-31 10:49:49 TarkuNi 阅读数 5087

最近在做微信公众号的开发,有个下载发票pdf文件的功能,这个pdf文件在服务器上,需要下载到手机本地,处理流程是,页面列表里每项都有一个下载按钮,点击下载按钮,跳到新的下载页面,下载页面代码如下:

<body>
<a id="path" href="" ><span id="spanId">下一步</span></a>
<script type="text/javascript">
require(['jquery','common'], function($){
$("#path").attr("href","${billPath}");
$("#spanId").click();
});
</script>
</body>

开发期间走了不少弯路,例如当时只认为有<a id="down" href=""></a>然后js控制自动触发$("#down").click();就行了

事实证明,不可以用,我以为js写错了,换成:$("#down").trigger("click");还是不行,以为自动执行顺序错了,没加载完就执行了,又换了onload方法,

最后看到一个大神写的简单的帖子,<a>里面加<span>标签,触发<span>的click事件,特此小记,感谢大神

2016-12-08 15:19:30 you18131371836 阅读数 1839

一,为什么要用到ngrok?

       各位肯定都知道,做微信开发,我们的开发服务器需要和微信服务器做交互,我们需要准备一台放置在公网的服务器,能够使得我们的服务器可以正常访问微信服务器,并且微信服务器也需要可以正常访问我们的开发服务器。并且我们做开发时的服务器都是放在内网中的,只能通过内网来访问。然而微信服务器无法和我们的内网服务器做交互,这就导致我们做微信开发在本地调试出现了一定的困难!

     今天就向大家介绍一个非常强大的外网映射工具:ngrok.它可以把你的本地ip(127.0.0.1|localhost)映射成一个公网域名,这样所有人都可以通过这个域名来访问你的项目,这个功能对本地调试,特别是微信公众号开发带来了极大的便利.

示例:
http://localhost:8080/项目名/ -> http://**.ngrok.cc/项目名

提醒:微信接口服务只支持80端口,并不支持其他的端口

,准备开发工具

     1.tomcat

     2.ngrok客户端(可以访问官网进行下载,官网地址是:http://www.ngrok.cc)

三,ngrok版本说明

    

此处要介绍三个ngrok版本:ngrok,tunnel,sunny-ngrok.

第一个版本是国外开发的原生ngrok版本,这个版本将我们的本地ip映射到一个二级域名上,这个域名有两种形式:自定义主机名和系统分配主机名(主机名为最左边的部分,比如我们平时经常看到的www).这个版本有两个缺陷:
1.系统分配的主机名是动态分配的,即每一次开启都会不一样,所以每次访问的时候都要记住不一样的域名,这很蛋疼.
2.这个域名是在国外的,所以访问速度会相当慢

第二个版本是国内基于ngrok开发的一个改良版,由于域名在国内,所以解决了访问慢的问题,但是这个版本现在已经无法使用

第三个版本也是国内开发出的一个版本,可以自定义主机名,还可以自定义域名(前提是你有一条域名),很好的解决了原生ngrok的那两条缺陷.

,环境搭建步骤

         我们这里主要来介绍sunny-ngrok版本的搭建方法;

        1、访问ngrok授权管理系统(http://www.ngrok.cc/login),点击注册,注册帐号。

       

        2,注册一个新的账号,如下如所示:

        

 

         3,注册成功以后点击登录进入ngrok授权管理系统的主界面,然后点击左侧菜单栏中的隧道管理----》开通隧道,进入下面的界面,


          

     进入界面以后提供隧道的两种购买方式,一种是付费的,一种是免费(其实两者的区别就是带宽的不同,如果只是做测试用,10M带宽肯定够用)

     然后点击立即购买;将会出现下面的界面

 

      注意:1.隧道协议选择http协议;

                2.填写隧道名称以及前置域名

                3.把上面本地端口改成127.0.0.1:8080

    然后点击确定添加

    4.确定添加成功后跳转到新的界面下,继续点击左侧菜单中的隧道管理,出现下面的界面,并且要复制隧道id

  


  5.下载ngrok的客户端

     访问下面的网址http://www.ngrok.cc就可以下载客户端,如下图所示:


         下载到本地的D盘中,然后解压,如下图所示:





6.输入上文的隧道id,然后回车。如下图所示:



到此ngrok的环境搭建已经完成,下面就是测试,测试的时候在项目中启动tomcat,用http://127.0.0.1:8080/访问能访问到

同时用http://****.ngrok.cc/也能访问到;


7.最后一步就是把该域名配置到微信公众平台上就ok了,大功告成。。。。。。


2018-06-06 16:13:20 qq_33240000 阅读数 23640

我们在进行微信公众平台开发,基本都会遇到一个问题:配置服务器.....这个问题不大不小,还是挺繁琐的.......下图是配置完成后的样子,后面给大家详细介绍每一步的步骤:


下图为微信公众号开发验证机制:由用户发送请求给微信服务器,微信服务器根据地址URL访问请求个人开发服务器,然后个人开发服务器响应返回给微信服务器(其中有数据报文验证,Token字段验证是其中之一),微信服务器接收后响应给用户,整个过程完成无误后,即服务器配置完成....


第一步:申请个人服务器及域名

(1)腾讯云:https://cloud.tencent.com/

(2)阿里云:https://www.aliyun.com/

(3)华为云:https://www.huaweicloud.com/

以上提供云服务的公司都可以选择,其中域名备案通常需要一个月时间,三家公司都有一个共同点就是要钱,哈哈哈哈大哭......既然这么说了就给大家介绍一下免费的安静

(4)魔方云:https://www.mfyun.com.cn/act/freehost/ ,直戳链接免费领取一年虚拟云主机活动,虚拟云主机和云服务器的区别我就不介绍了,两者感觉都能满足个人对微信公众号的基本开发,虚拟云主机自带域名很合适短期用户需求


注册号云虚拟主机后,滚动至下方根据需求将数据库开启,这个活动免费赠送100M容量MySQL数据库

第二步:添加服务器地址URL


服务器地址URL为:http://或者https://加申请域名,填写之前可以先进行个人服务器访问测试,访问其根目录页面

第三步:填写令牌(Token)

微信服务器与个人服务器数据报验证是采用doGet形式进行验证,本文采用的是php代码验证


  验证代码中的token字符串要和微信服务器配置中的令牌(Token)一致

第四步:上传完整验证代码至个人服务器

ftp文件传输协议,将完整代码上传至个人云服务器或者云虚拟主机,本人采用的是notepad++的ftp插件,ftp插件下载和导入请自行百度。

4.1与个人服务器建立连接


其中Hostname为:云虚拟主机的上传地址,Username为ftp用户,password为ftp密码

连接成功即显示服务器中文件目录


上传完整代码:1.新建index.php文件2.将完整代码copy后点击保存,3.成功后可以看到目录出现上传文件


第五步:尝试启动微信服务器配置

将完整URL填入,这里是http://+域名+index.php,令牌Token值需要和代码中的token一样,消息加解密密匙可以点击随即获取,加解密方式选明文模式

到这里终于配置完服务器,但这只是总体开发万里长征中的第一步,路漫漫其修远,然上下而求索兮............

希望以上解释说明能对大家有所帮助,谢谢支持!

2017-09-04 01:44:20 qq_28506819 阅读数 90917

微信公众号开发(一)服务器及接口的配置

关于微信公众号中的订阅号和服务的区别这里不多加讨论,网上有很多资源可以搜到,这里直接进入正题,如果是个人开发者,这里建议使用测试号进行开发学习,测试号的权限要比个人订阅号要多的多,而本篇博客也是基于测试号进行开发的。

 

在开始微信号开发之前需要准备好两样东西,1、需要一个测试号,2、需要一个拥有域名的服务器,下面将分别介绍怎样获取这两样东西。

1、测试号

点击此链接测试号登录可直接用微信扫一扫注册一个测试号,相应的界面如下所示

 

进入测试号界面之后可以获得一些开发所需要的东西,以及关注者列表和开发权限等,如下所示

 

2、服务器

微信号开发需要一个第三方服务器来和微信服务器沟通,这里我使用的是一个百度云的BAE虚拟主机,理由有以下几点:

1、性价比高,该主机一天只需要2毛钱,而且不限制充值金额,如果你只是用来学习微信公众号开发,而且预计一个月学会的话,那么你只需要充值6元钱就可以,相比其他最低充值一年的平台相比,要划算的多。

2、可以免去购买域名的费用,该BAE可免费设置一个二级域名,可用于微信公众号开发,并且数据库的使用也是免费的。

3、该BAE的代码可以使用git或svn管理,我们在本地写好代码之后,直接push上去就可以了,相当方便,而且可以设置快捷发布,提交代码当即发布。

缺点就是它的日志系统不够完善,不过这个缺点很容易解决,我们可以自己打印信息到文件里,然后读取出来,下面将会介绍到。

 

百度云网址是:https://login.bce.baidu.com/?account=,登录之后,点击BAE后如图

 

好了,这样我们就准备好了微信开发的两个必备的东西。接下来就要配置接口了。

 

3、配置接口

上面进入测试号界面的时候我们可以看到有一个接口配置信息的模块,如下所示

 

 

这里的URL就是填写以上BAE中的域名就好了,当然了要注意加上http://前缀,以后用户发送的消息都会经过微信服务器转发到该接口。Token可以随便填写一些东西,这里填写了weixin。关于Token的用途下面会讲到。

好了,现在先别急着点击提交,因为我们还没有在URL指向的服务器里编写任何的代码,还不能正确响应微信服务器的请求。

 

在编写任何的代码之前,我先说一下本博客的一些习惯,为了便于理解,我会先将文件的结构和代码先贴出来,然后才解释具体代码的含义,这样如果熟悉的人就可以直接跳过该部分了。

 

文件结构如下

 

index.php用于处理消息。

output_log.php和output_query.php分别用来输出post过来的数据和请求的查询字符串,Utils.php主要用来将数据输出到文件中,看了下面的代码你就明白了,其实相当简单,这三个东西是我用来调试用的,相比起微信公众号提供的在线调试接口而言(需要设置一堆的信息),我觉得这样更加简单。

 

Utils.php,提供了两个函数,traceHttp()将请求的时间、远程主机地址和查询字符串输出到query.xml文件中。logger()将类型、时间和post数据输出到log.xml中。

<?php
class Utils
{
    public static function traceHttp()
    {
        $content = date('Y-m-d H:i:s')."\n\rremote_ip:".$_SERVER["REMOTE_ADDR"].
            "\n\r".$_SERVER["QUERY_STRING"]."\n\r\n\r";
        $max_size = 1000;
        $log_filename = "./query.xml";
        if (file_exists($log_filename) and (abs(filesize($log_filename))) > $max_size){
            unlink($log_filename);
        }else {

        }
        file_put_contents($log_filename, $content, FILE_APPEND);
    }

    public static function logger($log_content, $type = '用户')
    {
        $max_size = 3000;
        $log_filename = "./log.xml";
        if (file_exists($log_filename) and (abs(filesize($log_filename)) >
                $max_size)) {
            unlink($log_filename);
        }
        file_put_contents($log_filename, "$type  ".date('Y-m-d H:i:s')."\n\r".$log_content."\n\r",
            FILE_APPEND);
    }
}

 

output_query.php,输出query.xml的内容

<?php
@header('Content-type: text/plain;charset=UTF-8');
$filepath = './query.xml';
readfile($filepath);


output_log.php,输出log.xml的内容。

<?php
@header('Content-type: text/plain;charset=UTF-8');
$filepath = './log.xml';
readfile($filepath);


是不是非常简单,然后我们开始写处理消息index.php

<?php
//设置时区
date_default_timezone_set("Asia/Shanghai");
//定义TOKEN常量,这里的"weixin"就是在公众号里配置的TOKEN
define("TOKEN", "weixin");

require_once("Utils.php");
//打印请求的URL查询字符串到query.xml
Utils::traceHttp();

$wechatObj = new wechatCallBackapiTest();
/**
 * 如果有"echostr"字段,说明是一个URL验证请求,
 * 否则是微信用户发过来的信息
 */
if (isset($_GET["echostr"])){
    $wechatObj->valid();
}else {
    $wechatObj->responseMsg();
}

class wechatCallBackapiTest
{
    /**
     * 用于微信公众号里填写的URL的验证,
     * 如果合格则直接将"echostr"字段原样返回
     */
    public function valid()
    {
        $echoStr = $_GET["echostr"];
        if ($this->checkSignature()){
            echo $echoStr;
            exit;
        }
    }

    /**
     * 用于验证是否是微信服务器发来的消息
     * @return bool
     */
    private function checkSignature()
    {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];

        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);

        if ($tmpStr == $signature){
            return true;
        }else {
            return false;
        }
    }

    /**
     * 响应用户发来的消息
     */
    public function responseMsg()
    {
        //获取post过来的数据,它一个XML格式的数据
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
        //将数据打印到log.xml
        Utils::logger($postStr);
        if (!empty($postStr)){
            //将XML数据解析为一个对象
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $RX_TYPE = trim($postObj->MsgType);
            //消息类型分离
            switch($RX_TYPE){
                case "event":
                    $result = $this->receiveEvent($postObj);
                    break;
                default:
                    $result = "unknow msg type:".$RX_TYPE;
                    break;
            }
            //打印输出的数据到log.xml
            Utils::logger($result, '公众号');
            echo $result;
        }else{
            echo "";
            exit;
        }
    }

    /**
     * 接收事件消息
     */
    private function receiveEvent($object)
    {
        switch ($object->Event){
            //关注公众号事件
            case "subscribe":
                $content = "欢迎关注微微一笑很倾城";
                break;
            default:
                $content = "";
                break;
        }
        $result = $this->transmitText($object, $content);
        return $result;
    }

    /**
     * 回复文本消息
     */
    private function transmitText($object, $content)
    {
        $xmlTpl = "<xml>
    <ToUserName><![CDATA[%s]]></ToUserName>
    <FromUserName><![CDATA[%s]]></FromUserName>
    <CreateTime><![CDATA[%s]]></CreateTime>
    <MsgType><![CDATA[text]]></MsgType>
    <Content><![CDATA[%s]]></Content>
</xml>";
        $result = sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time(), $content);
        return $result;
    }
}


这几个文件写好之后,直接通过git push到BAE上,如果设置了快捷发布,过1、2秒钟就会自动发布了,状态里面会显示绿色的"正常",如果没有设置快捷发布,上传代码后需要手动点击右边的快捷发布按钮。

 

这时我们就可以点击测试号界面里面的那个提交按钮了。

 

 

如果配置正确,则会提示配置成功。

 

4、调试

在浏览器地址栏上输入,xxx/output_query.php,xxx是你的域名。则会出现你点击提交后发送过来的请求,类似如下

 

 

可以看到该查询字符串有4个字段

  • signature:微信加密签名
  • echostr:随机字符串
  • timestamp:时间戳
  • nonce:随机数

只有在验证URL的时候查询字符串中才会有“echostr”这个字段,验证的方法是

  1. 将token、timestamp、nonce三个参数进行字典序排序
  2. 将三个参数字符串拼接成一个字符串进行sha1加密
  3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

 

验证通过之后,我们可以用微信扫一扫,扫描测试号里面的公众号二维码,关注该公众号,关注之后,公众号列表会显示出关注者的列表。

 

此时微信应该会回复一条文本消息,也就是以上在index.php中设置的“欢迎关注XXX”。

 

这时我们重新刷新一下xxx/output_query.php,发现没有“echostr”这个字段了,因为这个是用户发来的消息,而不是验证URL的消息。多出来的openid字段是用户的微信号,如果采用的是加密模式,还会有encrypt_type和msg_signature等字段。测试号只有明文模式。

接着我们重新打开一个标签,访问XXX/log.xml,查看发送的post数据,下面是一个关注公众号的事件,和我们返回给微信的XML数据。

 

现在不比纠结这些数据格式的问题,以后我们会提到,这样我们的微信公众号开发就准备好了,记得把这两个日志URL保存为浏览器的标签方便下次访问,以后调试只要F5一下就可以了,是不是比微信提供的在线调试容易多了。

注意:必须在5秒内响应微信的服务器,否则会导致重传或者报错

 

下一章我们将会尝试微信的基本消息接口,那时候就会理解这些数据结构了。

相关博客

微信公众号开发(一)服务器及接口的配置

微信公众号开发(二)基础接口

微信公众号开发(三)获取access_token

微信公众号开发(四)自定义菜单

微信公众号开发(五)个性化菜单

微信公众号开发(六)素材管理

微信公众号开发(七)发送客服消息

微信公众号开发(八)用户管理

微信公众号开发(九)群发消息接口

微信公众号开发(十)模板消息

微信公众号开发(十一)生成带参数二维码

微信公众号开发(十二)OAuth2.0网页授权