前言:
在做这个功能之前,我以为会很难,心里有些抵制,在做的时候,发现确实有点为难,在此之前没有接触过,总觉得会很难,做的时候也证实了这一点,可昨晚以后却发现:在功能完成以后,好像本来就应该是这样,每一句都有存在的道理一点也不多,一点不能少。就像坐过山车,你永远会被那种感觉追着跑,然后我并没有坐过,哈哈哈
所以今天整理一下,发出来以供后者学习参考,并劝慰一句:没什么大不了的,决定去做,就已经做了大半了,只要把思路整理清晰,一切就水到渠成,剩下的就是你通过不断的思考,添加,修改,和测试了。
一,《讲参考及注意》
我做的时候参考的本站2位大神的代码,提别提出来感谢,指引的我的思路,完善的代码,感谢~~~~~~
1,http://blog.csdn.net/sun2015_07_24/article/details/51445844
给了我做这个功能的完整思路,但是文件下载到服务器的时候有问题,看品论是说下载请求的url写错了,但是改了还是不可 以················
2,http://blog.csdn.net/z834410038/article/details/73274586
这个就是我是用的图片下载到自己的服务器的。亲测可用!
二,《讲思路和代码》
1,微信js域名认证-----》配置微信config js,拿到微信返回的3个字段和自己的微信appid-----》按钮调起微信选择或者拍照的功能-------》图片上传到微信服务器-----------》把图片从微信下载来--------》讲下载的路径存入数据库,便于图片使用
2,操作可参照微信的文档,我们从通过config接口注入权限验证配置开始
wx.config({
debug : true,//测试是为true,可打印测试信息,完成后则为false
appId : "wx895ss4s4s54s5661", // 必填,公众号的唯一标识
timestamp :$("#timestamp").val() , // 必填,生成签名的时间戳
nonceStr : $("#nonceStr").val(), // 必填,生成签名的随机串
signature : $("#signature").val(), // 必填,签名,见附录1
jsApiList : [ "chooseImage", "uploadImage", "downloadImage" ] // 必填,需要使用的JS接口列表,
});
这里需要解释说明的就是这3个参数怎么来的?我看到的文档基本都是有说明的,但是在这里有2个点需要注意
a,你的测试电脑的(你的电脑)ip是会变的,很有可能你没法拿到acces_token,这个微信文档并没有说明,这里如果有了需要 在公众号里的功能设置里把自己的ip设置为白名单
b, 就是那个url,他说要你自动获取的,这个的意思就是你要填的url就是你要使用微信图片上传的那个页面,!!!!!
// 注意 URL 一定要动态获取,不能 hardcode
String url = "http://example.com";
这里我就是url搞不清楚,然后js调不起来,签名验证过不去,停了脚步,还不明白问题,耽误了时间
上代码:
a,js请求,拿到3个参数
wxCheckJs:function(){
$.ajax({
url : weixinUrl + "/wxconfig/main",
type: 'post',
dataType: 'json',
async: false,
success: function(data) {
document.getElementById("timestamp").value=data.timestamp;
document.getElementById("nonceStr").value=data.nonceStr;
document.getElementById("signature").value=data.signature;
},error : function(e) {
alert("请求错误!")
}
})
},
b,java 获取参数
这个url就是上边强调的url,一定不能写错,多个页面调用的话,可以通过js把url传过来,微信文档有说明获取url的方法
String url="http://www.example.com/project/app/weixin/html/addpic.html";
public class WxConfig {
@RequestMapping("/main")
@ResponseBody
public static Map<String, String> main(String[] args,HttpServletResponse response,HttpSession session) throws ParseException, IOException {
String jsapi_ticket = (String) session.getAttribute("ticket");
System.out.println("得到微信js授权的票据:"+jsapi_ticket);
String url="http://www.example.com/project/app/weixin/html/addpic.html";
response.setContentType("appliaction/json");
if(StringUtils.isBlank(jsapi_ticket)){
WeiXinController.getTicket(response, session);
}
//jsapi_ticket不存在的时候,通过判断去存,然后再取到作为参数使用
jsapi_ticket = (String) session.getAttribute("ticket");
Map<String, String> ret = sign(jsapi_ticket, url);
for (Map.Entry entry : ret.entrySet()) {
System.out.println(entry.getKey() + ", " + entry.getValue());
}
return ret;
};
@RequestMapping("/sign")
@ResponseBody
public static Map<String, String> sign(String jsapi_ticket, String url) {
Map<String, String> ret = new HashMap<String, String>();
String nonce_str = create_nonce_str();
String timestamp = create_timestamp();
String string1;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsapi_ticket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
System.out.println(string1);
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
ret.put("url", url);
ret.put("jsapi_ticket", jsapi_ticket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
return ret;
}
private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
private static String create_nonce_str() {
return UUID.randomUUID().toString();
}
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}
这里还有2个方法获取access_token和获取ticket 这里一般都不会有问题,但是我还是要给代码~
c,获取access_token:
Appid和secret我就不多说了,我这里是存到session,你可以自己做存储
public static String getAccessToken(HttpServletResponse response,HttpSession session){
String accessToken = null;
String url = access_token_url.replace("APPID", appid).replace("APPSECRET", secret);
String result = Tools.myHttpGetConnection(url);
response.setContentType("appliaction/json");
JSONObject dataJson = JSONObject.fromObject(result);
if(StringUtils.isNotBlank(result)){
accessToken = (String)dataJson.get("access_token");
session.setAttribute("access_token",accessToken);
System.out.println("获取的access_token:"+accessToken);
//设置当前的session的有效期 单位秒
session.setMaxInactiveInterval(115*60);
}else{
System.out.println("获取微信access_token出错!");
}
return accessToken;
}
获取ticket
d,根据access_token换取票据
public static String getTicket(HttpServletResponse response,HttpSession session) throws ParseException, IOException {
String ticket =null;
String accessToken= (String) session.getAttribute("access_token");
System.out.println("session里的accessToken:"+accessToken);
if(StringUtils.isBlank(accessToken)){
WeiXinController.getAccessToken(response, session);
}
//accessToken不存在的时候,通过判断去存,然后再取到作为参数使用
accessToken= (String) session.getAttribute("access_token");
String url = sign_ticket_create_url.replace("ACCESS_TOKEN",accessToken);
try {
String result = Tools.myHttpGetConnection(url);
response.setContentType("appliaction/json");
JSONObject dataJson = JSONObject.fromObject(result);
ticket= (String)dataJson.get("ticket");
session.setAttribute("ticket",ticket);
//设置当前的session的有效期 单位秒
session.setMaxInactiveInterval(115*60);
System.out.println("获取到的ticket:"+ticket);
}catch (Exception e) {
e.printStackTrace();
}
return ticket;
}
好了,经过以上操作就可以拿到那3个参数了,我这里是用了3个隐藏的框,将拿到的值放进去,然后再去配置config的
<input type="hidden" id="timestamp" />
<input type="hidden" id="nonceStr" />
<input type="hidden" id="signature" />
div class="picOpre">
<a οnclick="weiXinCheck.choseOrTake()" class="fileP">选择图片</a>
<a οnclick="weiXinCheck.deletePicture()" class="fileP">删除</a>
</div>
现在我们就可以专注于图片上传了,这个时候,我们在公众号里已经可以通过点击按钮进入相册选择照片获取拍照了
choseOrTake:function () {
wx.ready(function(){
//拍照或从手机相册中选图接口
wx.chooseImage({
count: 1, // 最多能选择多少张图片,默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
//上传图片接口
wx.uploadImage({
localId: localIds.toString(), // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function (res) {
var serverId = res.serverId; // 返回图片的服务器端ID
//alert("weixinserverId:"+serverId)
//在页面显示图片
var src = 'src="' + localIds + '"';
var html = "";
html += '<div id="nopictureImag" class="image-item ">';
html += '<img id="showImg" data-preview-src="" data-preview-group="1" ' + src + '/>';
html += '</div>';
$("#aa").html(html);
}, fail: function (res) {
alert('上传图片失败,请重试')
}
});
}
});
});
},
到这里,其实我们就已经完成了一大半,我们可以在相册里选择图片,可以在图片框里显示图片,但是我们的图片在微信的服务器里,我们还需要将图片下载下来,这里我就出现了最上边说的问题,下载到的是一个不知道怎么东西的文件所以我采用了第2中的下载方法
@RequestMapping("/savePicture")
@ResponseBody
public static String savePicture(String mediaId, HttpServletResponse response,HttpSession session,HttpServletRequest request){
String filename=null;
//mediaId="5HQSCcX9QoRjK64ayQ0qwH_L5vB5K65eDEbTF_e4LN4W4BWY5b3_Vr4mzg5RSfvY";
if(StringUtils.isNotBlank(mediaId)){
filename = saveImageToDisk(mediaId,session,response,request);
}
return filename;
}
/**
* 获取临时素材
*/
private static InputStream getMedia(String mediaId,HttpServletResponse response,HttpSession session) {
String url = "https://api.weixin.qq.com/cgi-bin/media/get";
String accessToken= (String) session.getAttribute("access_token");
System.out.println("session里的accessToken:"+accessToken);
if(StringUtils.isBlank(accessToken)){
WeiXinController.getAccessToken(response, session);
}
//accessToken不存在的时候,通过判断去存,然后再取到作为参数使用
accessToken= (String) session.getAttribute("access_token");
String params = "access_token=" + accessToken + "&media_id=" + mediaId;
InputStream is = null;
try {
String urlNameString = url + "?" + params;
System.out.println(urlNameString);
URL urlGet = new URL(urlNameString);
http = (HttpURLConnection) urlGet.openConnection();
http.setRequestMethod("GET"); // 必须是get方式请求
http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
http.setDoOutput(true);
http.setDoInput(true);
http.connect();
// 获取文件转化为byte流
is = http.getInputStream();
} catch (Exception e) {
e.printStackTrace();
}
return is;
}
/**
* 根据内容类型判断文件扩展名
*
* @param contentType 内容类型
* @return
*/
public static String getFileexpandedName(String contentType) {
String fileEndWitsh = "";
if ("image/jpeg".equals(contentType))
fileEndWitsh = ".jpg";
else if ("audio/mpeg".equals(contentType))
fileEndWitsh = ".mp3";
else if ("audio/amr".equals(contentType))
fileEndWitsh = ".amr";
else if ("video/mp4".equals(contentType))
fileEndWitsh = ".mp4";
else if ("video/mpeg4".equals(contentType))
fileEndWitsh = ".mp4";
return fileEndWitsh;
}
/**
* 保存图片至服务器
* @param mediaId
* @return 文件名
*/
public static String saveImageToDisk(String mediaId,HttpSession session,HttpServletResponse response,HttpServletRequest request){
String filename = "";
String savePath = "/GHimage/upload/";
InputStream inputStream = getMedia(mediaId, response, session);
byte[] data = new byte[1024];
int len = 0;
FileOutputStream fileOutputStream = null;
try {
//服务器存图路径
String realPath = request.getSession().getServletContext().getRealPath("");
String webPath = request.getContextPath();
webPath=webPath.substring(1,webPath.length());
realPath=realPath.replaceAll(webPath, savePath);//把真实路径替换为自己定义的路径下
//文件格式转换
String fileExt = WeiXinController.getFileexpandedName(http.getHeaderField("Content-Type"));
//filename =System.currentTimeMillis()+".jpg";
filename =System.currentTimeMillis()+fileExt;
fileOutputStream = new FileOutputStream(realPath + filename);
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();
}
}
}
System.out.println(filename);
return filename;
}
这里返回的就是文件存储的地址,这里我们拿到就可以了,但是这个时候,我们应该把文件的路径存起来,这样我们还可以通过路径显示出来
需要说明的是。我们文件上传后会获得文件的
var serverId = res.serverId; // 返回图片的服务器端ID
文件下载的时候就是
mediaId
我们要通过一个方法将serverid传过去,在java里写个方法接受severid就是mediaid,在这个方法里引用图片下载方法
Repair repair = new Repair();
if(StringUtils.isNotBlank(mediaId)){
String filePath=WeiXinController.savePicture(mediaId, response, session, request);
filePath="/"+uploadPath+filePath;
repair.setPictureImg(filePath);
}
三《总结与思考》
好了,到这里我们的上传和下载就已经做好了,代码给的很详细,工具类也是,如果还有不明白的可以私信我,我会很快回你的,思路也是比较清晰的,我自己的项目最终也是这个代码,拿去基本不改就可以用,
其实呢,代码贴出来以后就发现其实很简单,可我这个流程做了一周左右,中间遇到很多问题无非就是ip没有添加拿不到access_token,还有url填写不对,拿到的签名无法验证,还有就是下载到的图片不知道是什么东西,后来一点点的百度,一点点的解决,周六还加了半天的班。
年轻人,未来总是有很多不幸,买房、结婚、买车,自己也能力有限,拿着父母的血汗钱,努力在大城市站住脚,很多很多的不容易,我不是没有考虑过自己的未来,只是在200w的房价面前,自己真的有点压力,毕竟还有结婚、父母、孩子要考虑,好了不多说了代码拿去吧,可以用,有问题私信我