一、本節要點
1.獲取臨時素材接口
請求方式:GET(HTTPS)
請求地址:https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
2.獲取臨時素材接口的返回結果
企業微信官方開發文檔中說明的返回結果如下:
若你以為這就是返回結果,然后跟之前一樣,先訪問接口,從http連接的輸入流中的獲取回結果的文本內容,你會發現你接收到的結果是一堆亂碼。
這是為何?
以圖片為例,此處千萬要注意,微信返回的結果是一個文件流形式的圖片,當我們從http連接的輸入流中的獲取回結果的文本內容,也就是獲取圖片的文本內容時,當然就是一堆亂碼了。
這就好比你用記事本打開一張圖片,然后發現內容是一片亂碼。這再正常不過。所以我們接受圖片的時候不能只接收文本數據,而是要接收流。
千萬得注意:獲取臨時素材時,微信返回的結果是一個流形式的臨時素材。
我們需要做的就是調用接口,獲取http連接的輸入流中數據,再將輸入流中的數據寫入到輸出流,再通過輸出流生成一張圖片。這張圖片就是微信返回的臨時素材了。
3.下載文件時的文件路徑
request.getSession().getServletContext().getRealPath("").replaceAll("\\\\", "/")
將獲取路徑:%TOMCAT_HOME%/webapp/工程名。若不是這個路徑就請參考:Eclipse中的Web項目自動部署到Tomcat
String savePath=request.getSession().getServletContext().getRealPath("").replaceAll("\\\\", "/")+"/img/";
將獲取路徑:%TOMCAT_HOME%/webapp/工程名/img/
即從微信服務器下載的圖片都保存在這個路徑下。
二、代碼實現
這里承接上一節。在上一節中我們完成了JSSDK的配置,並且用圖片上傳接口將圖片上傳到了微信服務器。這一節我們需要做的就是在圖片上傳到微信服務器后,根據微信服務器返回的serverId(即mediaId)來調用獲取臨時素材的接口,進行臨時素材的下載,並保存到本地指定的路徑下。
2.1 UploadExpenseAccaoutServlet

package com.ray.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ray.service.TempMaterialService; import com.ray.util.WeiXinParamesUtil; import com.ray.util.WeiXinUtil; /** * Servlet implementation class UploadExpenseAccaoutServlet */ @WebServlet("/UploadExpenseAccaoutServlet") public class UploadExpenseAccaoutServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public UploadExpenseAccaoutServlet() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub String mediaId=request.getParameter("serverId"); System.out.println("serverId:"+mediaId); String accessToken= WeiXinUtil.getAccessToken(WeiXinParamesUtil.corpId, WeiXinParamesUtil.contactsSecret).getToken(); System.out.println("accessToken:"+accessToken); //String savePath=System.getProperty("user.dir").replaceAll("\\\\", "/")+"/WebContent/img/"+mediaId+".png"; String savePath=request.getSession().getServletContext().getRealPath("").replaceAll("\\\\", "/")+"/img/"; System.out.println("savePath:"+savePath); //2.調用業務類,獲取臨時素材 TempMaterialService tms=new TempMaterialService(); tms.getTempMaterial(accessToken, mediaId,savePath); PrintWriter out = response.getWriter(); out.print("HHHHHHHHHH"); out.close(); out = null; } }
在此servlet中
(1)接收serverId
(2)調用臨時素材業務類的方法TempMaterialService.getTempMaterial(accessToken, mediaId,savePath),獲取臨時素材。
2.2 臨時素材業務類—TempMaterialService

package com.ray.service; import java.io.File; import java.io.UnsupportedEncodingException; import com.ray.util.WeiXinUtil; import net.sf.json.JSONObject; /**@desc : 臨時素材業務類 * * @author: shirayner * @date : 2017-8-18 下午2:07:25 */ public class TempMaterialService { //上傳臨時素材url public String uploadTempMaterial_url="https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE"; //獲取臨時素材url public String getTempMaterial_url="https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID"; /** * @desc :上傳臨時素材 * * @param accessToken 接口訪問憑證 * @param type 媒體文件類型,分別有圖片(image)、語音(voice)、視頻(video),普通文件(file) * @param fileUrl 本地文件的url。例如 "D/1.img"。 * @return JSONObject 上傳成功后,微信服務器返回的參數,有type、media_id 、created_at */ public JSONObject uploadTempMaterial(String accessToken,String type,String fileUrl){ //1.創建本地文件 File file=new File(fileUrl); //2.拼接請求url uploadTempMaterial_url = uploadTempMaterial_url.replace("ACCESS_TOKEN", accessToken) .replace("TYPE", type); //3.調用接口,發送請求,上傳文件到微信服務器 String result=WeiXinUtil.httpRequest(uploadTempMaterial_url, file); //4.json字符串轉對象:解析返回值,json反序列化 result = result.replaceAll("[\\\\]", ""); System.out.println("result:" + result); JSONObject resultJSON = JSONObject.fromObject(result); //5.返回參數判斷 if (resultJSON != null) { if (resultJSON.get("media_id") != null) { System.out.println("上傳" + type + "臨時素材成功:"+resultJSON.get("media_id")); return resultJSON; } else { System.out.println("上傳" + type + "臨時素材成功失敗"); } } return null; } /** 2.獲取臨時素材 * * @param accessToken * @param mediaId * @return * @throws UnsupportedEncodingException */ public void getTempMaterial(String accessToken,String mediaId,String savePath) throws UnsupportedEncodingException{ //String savePath=System.getProperty("user.dir").replaceAll("\\\\", "/")+"/WebContent/img/"+mediaId+".png"; //System.out.println("service savePath:"+savePath); //1.拼接請求url getTempMaterial_url=getTempMaterial_url.replace("ACCESS_TOKEN", accessToken) .replace("MEDIA_ID", mediaId); savePath=savePath+mediaId; //2.調用接口,發送請求,獲取臨時素材 File file=null; try { file = WeiXinUtil.getFile(getTempMaterial_url,savePath); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("file:"+file.getName()); } }
在此類的獲取臨時素材方法中:
(1)拼接微信獲取臨時素材的接口url
(2)調用WeiXinUtil.getFile(getTempMaterial_url,savePath),向微信發起https請求,並將接收到的圖片下載到savePath指定的路徑下
2.3 微信工具類—WeiXinUtil

package com.ray.util; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Formatter; import java.util.HashMap; import java.util.Map; import java.util.UUID; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ray.pojo.AccessToken; import net.sf.json.JSONException; import net.sf.json.JSONObject; public class WeiXinUtil { private static Logger log = LoggerFactory.getLogger(WeiXinUtil.class); //微信的請求url //獲取access_token的接口地址(GET) 限200(次/天) public final static String access_token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpId}&corpsecret={corpsecret}"; //獲取jsapi_ticket的接口地址(GET) 限200(次/天) public final static String jsapi_ticket_url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=ACCESSTOKEN"; /** * 1.發起https請求並獲取結果 * * @param requestUrl 請求地址 * @param requestMethod 請求方式(GET、POST) * @param outputStr 提交的數據 * @return JSONObject(通過JSONObject.get(key)的方式獲取json對象的屬性值) */ public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; StringBuffer buffer = new StringBuffer(); try { // 創建SSLContext對象,並使用我們指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 從上述SSLContext對象中得到SSLSocketFactory對象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // 設置請求方式(GET/POST) httpUrlConn.setRequestMethod(requestMethod); if ("GET".equalsIgnoreCase(requestMethod)) httpUrlConn.connect(); // 當有數據需要提交時 if (null != outputStr) { OutputStream outputStream = httpUrlConn.getOutputStream(); // 注意編碼格式,防止中文亂碼 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 將返回的輸入流轉換成字符串 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 釋放資源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); jsonObject = JSONObject.fromObject(buffer.toString()); } catch (ConnectException ce) { log.error("Weixin server connection timed out."); } catch (Exception e) { log.error("https request error:{}", e); } return jsonObject; } /** * 2.發送https請求之獲取臨時素材 * @param requestUrl * @param savePath 文件的保存路徑,此時還缺一個擴展名 * @return * @throws Exception */ public static File getFile(String requestUrl,String savePath) throws Exception { //String path=System.getProperty("user.dir")+"/img//1.png"; // 創建SSLContext對象,並使用我們指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 從上述SSLContext對象中得到SSLSocketFactory對象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // 設置請求方式(GET/POST) httpUrlConn.setRequestMethod("GET"); httpUrlConn.connect(); //獲取文件擴展名 String ext=getExt(httpUrlConn.getContentType()); savePath=savePath+ext; System.out.println("savePath"+savePath); //下載文件到f文件 File file = new File(savePath); // 獲取微信返回的輸入流 InputStream in = httpUrlConn.getInputStream(); //輸出流,將微信返回的輸入流內容寫到文件中 FileOutputStream out = new FileOutputStream(file); int length=100*1024; byte[] byteBuffer = new byte[length]; //存儲文件內容 int byteread =0; int bytesum=0; while (( byteread=in.read(byteBuffer)) != -1) { bytesum += byteread; //字節數 文件大小 out.write(byteBuffer,0,byteread); } System.out.println("bytesum: "+bytesum); in.close(); // 釋放資源 out.close(); in = null; out=null; httpUrlConn.disconnect(); return file; } /** * @desc :2.微信上傳素材的請求方法 * * @param requestUrl 微信上傳臨時素材的接口url * @param file 要上傳的文件 * @return String 上傳成功后,微信服務器返回的消息 */ public static String httpRequest(String requestUrl, File file) { StringBuffer buffer = new StringBuffer(); try{ //1.建立連接 URL url = new URL(requestUrl); HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection(); //打開鏈接 //1.1輸入輸出設置 httpUrlConn.setDoInput(true); httpUrlConn.setDoOutput(true); httpUrlConn.setUseCaches(false); // post方式不能使用緩存 //1.2設置請求頭信息 httpUrlConn.setRequestProperty("Connection", "Keep-Alive"); httpUrlConn.setRequestProperty("Charset", "UTF-8"); //1.3設置邊界 String BOUNDARY = "----------" + System.currentTimeMillis(); httpUrlConn.setRequestProperty("Content-Type","multipart/form-data; boundary="+ BOUNDARY); // 請求正文信息 // 第一部分: //2.將文件頭輸出到微信服務器 StringBuilder sb = new StringBuilder(); sb.append("--"); // 必須多兩道線 sb.append(BOUNDARY); sb.append("\r\n"); sb.append("Content-Disposition: form-data;name=\"media\";filelength=\"" + file.length() + "\";filename=\""+ file.getName() + "\"\r\n"); sb.append("Content-Type:application/octet-stream\r\n\r\n"); byte[] head = sb.toString().getBytes("utf-8"); // 獲得輸出流 OutputStream outputStream = new DataOutputStream(httpUrlConn.getOutputStream()); // 將表頭寫入輸出流中:輸出表頭 outputStream.write(head); //3.將文件正文部分輸出到微信服務器 // 把文件以流文件的方式 寫入到微信服務器中 DataInputStream in = new DataInputStream(new FileInputStream(file)); int bytes = 0; byte[] bufferOut = new byte[1024]; while ((bytes = in.read(bufferOut)) != -1) { outputStream.write(bufferOut, 0, bytes); } in.close(); //4.將結尾部分輸出到微信服務器 byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定義最后數據分隔線 outputStream.write(foot); outputStream.flush(); outputStream.close(); //5.將微信服務器返回的輸入流轉換成字符串 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 釋放資源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); } catch (IOException e) { System.out.println("發送POST請求出現異常!" + e); e.printStackTrace(); } return buffer.toString(); } /** * 2.發起http請求獲取返回結果 * * @param requestUrl 請求地址 * @return */ public static String httpRequest(String requestUrl) { StringBuffer buffer = new StringBuffer(); try { URL url = new URL(requestUrl); HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection(); httpUrlConn.setDoOutput(false); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); httpUrlConn.setRequestMethod("GET"); httpUrlConn.connect(); // 將返回的輸入流轉換成字符串 InputStream inputStream = httpUrlConn.getInputStream(); //InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 釋放資源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); } catch (Exception e) { } return buffer.toString(); } /** * 3.獲取access_token * * @param appid 憑證 * @param appsecret 密鑰 * @return */ public static AccessToken getAccessToken(String appid, String appsecret) { AccessToken accessToken = null; String requestUrl = access_token_url.replace("{corpId}", appid).replace("{corpsecret}", appsecret); JSONObject jsonObject = httpRequest(requestUrl, "GET", null); // 如果請求成功 if (null != jsonObject) { try { accessToken = new AccessToken(); accessToken.setToken(jsonObject.getString("access_token")); accessToken.setExpiresIn(jsonObject.getInt("expires_in")); } catch (JSONException e) { accessToken = null; // 獲取token失敗 log.error("獲取token失敗 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return accessToken; } /** * 4. 獲取JsapiTicket * @param accessToken * @return */ public static String getJsapiTicket(String accessToken){ String requestUrl = jsapi_ticket_url.replace("ACCESSTOKEN", accessToken); JSONObject jsonObject = httpRequest(requestUrl, "GET", null); String jsapi_ticket=""; // 如果請求成功 if (null != jsonObject) { try { jsapi_ticket=jsonObject.getString("ticket"); } catch (JSONException e) { // 獲取token失敗 log.error("獲取token失敗 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return jsapi_ticket; } /** * 3.獲取企業微信的JSSDK配置信息 * @param request * @return */ public static Map<String, Object> getWxConfig(HttpServletRequest request) { Map<String, Object> ret = new HashMap<String, Object>(); //1.准備好參與簽名的字段 String nonceStr = UUID.randomUUID().toString(); // 必填,生成簽名的隨機串 //System.out.println("nonceStr:"+nonceStr); String accessToken=WeiXinUtil.getAccessToken(WeiXinParamesUtil.corpId, WeiXinParamesUtil.agentSecret).getToken(); String jsapi_ticket =getJsapiTicket(accessToken);// 必填,生成簽名的H5應用調用企業微信JS接口的臨時票據 //System.out.println("jsapi_ticket:"+jsapi_ticket); String timestamp = Long.toString(System.currentTimeMillis() / 1000); // 必填,生成簽名的時間戳 //System.out.println("timestamp:"+timestamp); String url=request.getRequestURL().toString(); //System.out.println("url:"+url); //2.字典序 ,注意這里參數名必須全部小寫,且必須有序 String sign = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonceStr+ "×tamp=" + timestamp + "&url=" + url; //3.sha1簽名 String signature = ""; try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(sign.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); //System.out.println("signature:"+signature); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ret.put("appId", WeiXinParamesUtil.corpId); ret.put("timestamp", timestamp); ret.put("nonceStr", nonceStr); ret.put("signature", signature); return ret; } /** * 方法名:byteToHex</br> * 詳述:字符串加密輔助方法 </br> * 開發人員:souvc </br> * 創建時間:2016-1-5 </br> * @param hash * @return 說明返回值含義 * @throws 說明發生此異常的條件 */ 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 getExt(String contentType){ if("image/jpeg".equals(contentType)){ return ".jpg"; }else if("image/png".equals(contentType)){ return ".png"; }else if("image/gif".equals(contentType)){ return ".gif"; } return null; } }
獲取臨時素材的方法為:WeiXinUtil.getFile(String requestUrl,String savePath)
在此方法中:
(1)發起https請求,獲取輸入流
(2)從輸入流中獲取文件類型,與savePath一起組成圖片最終的路徑(或者說是文件名A)
(3)根據文件名A創建輸出流
(4)將輸入流中的數據寫入到輸出流中,這樣圖片就保存到了文件A中。
(5)返回文件A