@@ -26,7 +26,6 @@ public class WxConsts { | |||||
public static final String MEDIA_THUMB = "thumb"; | public static final String MEDIA_THUMB = "thumb"; | ||||
public static final String FILE_JPG = "jpeg"; | public static final String FILE_JPG = "jpeg"; | ||||
public static final String FILE_PNG = "png"; | |||||
public static final String FILE_MP3 = "mp3"; | public static final String FILE_MP3 = "mp3"; | ||||
public static final String FILE_ARM = "arm"; | public static final String FILE_ARM = "arm"; | ||||
public static final String FILE_MP4 = "mp4"; | public static final String FILE_MP4 = "mp4"; | ||||
@@ -44,6 +44,7 @@ public interface WxService { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 上传多媒体文件 | * 上传多媒体文件 | ||||
* | |||||
* 上传的多媒体文件有格式和大小限制,如下: | * 上传的多媒体文件有格式和大小限制,如下: | ||||
* 图片(image): 1M,支持JPG格式 | * 图片(image): 1M,支持JPG格式 | ||||
* 语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式 | * 语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式 | ||||
@@ -67,6 +68,18 @@ public interface WxService { | |||||
*/ | */ | ||||
public WxUploadResult uploadMedia(String mediaType, File file) throws WxErrorException; | public WxUploadResult uploadMedia(String mediaType, File file) throws WxErrorException; | ||||
/** | |||||
* <pre> | |||||
* 下载多媒体文件 | |||||
* | |||||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件 | |||||
* </pre> | |||||
* @params media_id | |||||
* @return 保存到本地的临时文件 | |||||
* @throws WxErrorException | |||||
*/ | |||||
public File downloadMedia(String media_id) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 发送客服消息 | * 发送客服消息 | ||||
@@ -75,7 +88,7 @@ public interface WxService { | |||||
* @param message | * @param message | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
public String sendCustomMessage(WxCustomMessage message) throws WxErrorException; | |||||
public void sendCustomMessage(WxCustomMessage message) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -85,7 +98,7 @@ public interface WxService { | |||||
* @param menu | * @param menu | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
public String createMenu(WxMenu menu) throws WxErrorException; | |||||
public void createMenu(WxMenu menu) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -94,7 +107,7 @@ public interface WxService { | |||||
* </pre> | * </pre> | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
public String deleteMenu() throws WxErrorException; | |||||
public void deleteMenu() throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -1,7 +1,6 @@ | |||||
package chanjarster.weixin.api; | package chanjarster.weixin.api; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileOutputStream; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.security.MessageDigest; | import java.security.MessageDigest; | ||||
@@ -10,15 +9,9 @@ import java.util.UUID; | |||||
import java.util.concurrent.atomic.AtomicBoolean; | import java.util.concurrent.atomic.AtomicBoolean; | ||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import org.apache.http.Consts; | |||||
import org.apache.http.HttpEntity; | |||||
import org.apache.http.client.ClientProtocolException; | import org.apache.http.client.ClientProtocolException; | ||||
import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||
import org.apache.http.client.methods.HttpGet; | import org.apache.http.client.methods.HttpGet; | ||||
import org.apache.http.client.methods.HttpPost; | |||||
import org.apache.http.entity.ContentType; | |||||
import org.apache.http.entity.StringEntity; | |||||
import org.apache.http.entity.mime.MultipartEntityBuilder; | |||||
import org.apache.http.impl.client.BasicResponseHandler; | import org.apache.http.impl.client.BasicResponseHandler; | ||||
import org.apache.http.impl.client.CloseableHttpClient; | import org.apache.http.impl.client.CloseableHttpClient; | ||||
import org.apache.http.impl.client.HttpClients; | import org.apache.http.impl.client.HttpClients; | ||||
@@ -29,7 +22,12 @@ import chanjarster.weixin.bean.WxMenu; | |||||
import chanjarster.weixin.bean.result.WxError; | import chanjarster.weixin.bean.result.WxError; | ||||
import chanjarster.weixin.bean.result.WxUploadResult; | import chanjarster.weixin.bean.result.WxUploadResult; | ||||
import chanjarster.weixin.exception.WxErrorException; | import chanjarster.weixin.exception.WxErrorException; | ||||
import chanjarster.weixin.util.Utf8ResponseHandler; | |||||
import chanjarster.weixin.util.fs.FileUtil; | |||||
import chanjarster.weixin.util.http.MediaDownloadRequestExecutor; | |||||
import chanjarster.weixin.util.http.MediaUploadRequestExecutor; | |||||
import chanjarster.weixin.util.http.RequestExecutor; | |||||
import chanjarster.weixin.util.http.SimpleGetRequestExecutor; | |||||
import chanjarster.weixin.util.http.SimplePostRequestExecutor; | |||||
public class WxServiceImpl implements WxService { | public class WxServiceImpl implements WxService { | ||||
@@ -62,7 +60,7 @@ public class WxServiceImpl implements WxService { | |||||
} | } | ||||
} | } | ||||
protected static String bytesToHex(byte[] b) { | |||||
protected String bytesToHex(byte[] b) { | |||||
char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7', | char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7', | ||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; | ||||
StringBuffer buf = new StringBuffer(); | StringBuffer buf = new StringBuffer(); | ||||
@@ -110,25 +108,25 @@ public class WxServiceImpl implements WxService { | |||||
} | } | ||||
} | } | ||||
public String sendCustomMessage(WxCustomMessage message) throws WxErrorException { | |||||
public void sendCustomMessage(WxCustomMessage message) throws WxErrorException { | |||||
String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; | String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; | ||||
return post(url, message.toJson()); | |||||
execute(new SimplePostRequestExecutor(), url, message.toJson()); | |||||
} | } | ||||
public String createMenu(WxMenu menu) throws WxErrorException { | |||||
public void createMenu(WxMenu menu) throws WxErrorException { | |||||
String url = "https://api.weixin.qq.com/cgi-bin/menu/create"; | String url = "https://api.weixin.qq.com/cgi-bin/menu/create"; | ||||
return post(url, menu.toJson()); | |||||
execute(new SimplePostRequestExecutor(), url, menu.toJson()); | |||||
} | } | ||||
public String deleteMenu() throws WxErrorException { | |||||
public void deleteMenu() throws WxErrorException { | |||||
String url = "https://api.weixin.qq.com/cgi-bin/menu/delete"; | String url = "https://api.weixin.qq.com/cgi-bin/menu/delete"; | ||||
return get(url, null); | |||||
execute(new SimpleGetRequestExecutor(), url, null); | |||||
} | } | ||||
public WxMenu getMenu() throws WxErrorException { | public WxMenu getMenu() throws WxErrorException { | ||||
String url = "https://api.weixin.qq.com/cgi-bin/menu/get"; | String url = "https://api.weixin.qq.com/cgi-bin/menu/get"; | ||||
try { | try { | ||||
String resultContent = get(url, null); | |||||
String resultContent = execute(new SimpleGetRequestExecutor(), url, null); | |||||
return WxMenu.fromJson(resultContent); | return WxMenu.fromJson(resultContent); | ||||
} catch (WxErrorException e) { | } catch (WxErrorException e) { | ||||
// 46003 不存在的菜单数据 | // 46003 不存在的菜单数据 | ||||
@@ -140,30 +138,28 @@ public class WxServiceImpl implements WxService { | |||||
} | } | ||||
public WxUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException { | public WxUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException { | ||||
return uploadMedia(mediaType, createTmpFile(inputStream, fileType)); | |||||
return uploadMedia(mediaType,FileUtil.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); | |||||
} | } | ||||
public WxUploadResult uploadMedia(String mediaType, File file) throws WxErrorException { | public WxUploadResult uploadMedia(String mediaType, File file) throws WxErrorException { | ||||
String url = "http://file.api.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType; | String url = "http://file.api.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType; | ||||
String json = post(url, file); | |||||
return WxUploadResult.fromJson(json); | |||||
return execute(new MediaUploadRequestExecutor(), url, file); | |||||
} | } | ||||
protected String post(String uri, Object data) throws WxErrorException { | |||||
return execute("POST", uri, data); | |||||
} | |||||
protected String get(String uri, Object data) throws WxErrorException { | |||||
return execute("GET", uri, data); | |||||
public File downloadMedia(String media_id) throws WxErrorException { | |||||
String url = "http://file.api.weixin.qq.com/cgi-bin/media/get"; | |||||
return execute(new MediaDownloadRequestExecutor(), url, "media_id=" + media_id); | |||||
} | } | ||||
/** | /** | ||||
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 | * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 | ||||
* @param request | |||||
* @return 微信服务端返回的结果 | |||||
* @throws WxErrorException | |||||
* @param executor | |||||
* @param uri | |||||
* @param data | |||||
* @return | |||||
* @throws WxErrorException | |||||
*/ | */ | ||||
protected String execute(String method, String uri, Object data) throws WxErrorException { | |||||
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | |||||
if (StringUtils.isBlank(wxConfigStorage.getAccessToken())) { | if (StringUtils.isBlank(wxConfigStorage.getAccessToken())) { | ||||
refreshAccessToken(); | refreshAccessToken(); | ||||
} | } | ||||
@@ -173,38 +169,9 @@ public class WxServiceImpl implements WxService { | |||||
uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; | uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; | ||||
try { | try { | ||||
String resultContent = null; | |||||
if ("POST".equals(method)) { | |||||
HttpPost httpPost = new HttpPost(uriWithAccessToken); | |||||
if (data != null) { | |||||
if (data instanceof String) { | |||||
StringEntity entity = new StringEntity((String)data, Consts.UTF_8); | |||||
httpPost.setEntity(entity); | |||||
} | |||||
if (data instanceof File) { | |||||
File file = (File) data; | |||||
HttpEntity entity = MultipartEntityBuilder | |||||
.create() | |||||
.addBinaryBody("media", file) | |||||
.build(); | |||||
httpPost.setEntity(entity); | |||||
httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); | |||||
} | |||||
} | |||||
CloseableHttpResponse response = httpclient.execute(httpPost); | |||||
resultContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | |||||
} else if ("GET".equals(method)) { | |||||
if (data != null) { | |||||
if (data instanceof String) { | |||||
uriWithAccessToken += uriWithAccessToken.endsWith("&") ? data : '&' + (String)data; | |||||
} | |||||
} | |||||
HttpGet httpGet = new HttpGet(uriWithAccessToken); | |||||
CloseableHttpResponse response = httpclient.execute(httpGet); | |||||
resultContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | |||||
} | |||||
WxError error = WxError.fromJson(resultContent); | |||||
return executor.execute(uriWithAccessToken, data); | |||||
} catch (WxErrorException e) { | |||||
WxError error = e.getError(); | |||||
/* | /* | ||||
* 发生以下情况时尝试刷新access_token | * 发生以下情况时尝试刷新access_token | ||||
* 40001 获取access_token时AppSecret错误,或者access_token无效 | * 40001 获取access_token时AppSecret错误,或者access_token无效 | ||||
@@ -212,12 +179,24 @@ public class WxServiceImpl implements WxService { | |||||
*/ | */ | ||||
if (error.getErrcode() == 42001 || error.getErrcode() == 40001) { | if (error.getErrcode() == 42001 || error.getErrcode() == 40001) { | ||||
refreshAccessToken(); | refreshAccessToken(); | ||||
return execute(method, uri, data); | |||||
return execute(executor, uri, data); | |||||
} | |||||
/** | |||||
* -1 系统繁忙, 500ms后重试 | |||||
*/ | |||||
if (error.getErrcode() == -1) { | |||||
try { | |||||
System.out.println("微信系统繁忙,500ms后重试"); | |||||
Thread.sleep(500); | |||||
return execute(executor, uri, data); | |||||
} catch (InterruptedException e1) { | |||||
throw new RuntimeException(e1); | |||||
} | |||||
} | } | ||||
if (error.getErrcode() != 0) { | if (error.getErrcode() != 0) { | ||||
throw new WxErrorException(error); | throw new WxErrorException(error); | ||||
} | } | ||||
return resultContent; | |||||
return null; | |||||
} catch (ClientProtocolException e) { | } catch (ClientProtocolException e) { | ||||
throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
@@ -225,38 +204,8 @@ public class WxServiceImpl implements WxService { | |||||
} | } | ||||
} | } | ||||
protected File createTmpFile(InputStream inputStream, String fileType) throws IOException { | |||||
FileOutputStream fos = null; | |||||
try { | |||||
File tmpFile = File.createTempFile(UUID.randomUUID().toString(), '.' + fileType); | |||||
tmpFile.deleteOnExit(); | |||||
fos = new FileOutputStream(tmpFile); | |||||
int read = 0; | |||||
byte[] bytes = new byte[1024 * 100]; | |||||
while ((read = inputStream.read(bytes)) != -1) { | |||||
fos.write(bytes, 0, read); | |||||
} | |||||
fos.flush(); | |||||
return tmpFile; | |||||
} finally { | |||||
if (inputStream != null) { | |||||
try { | |||||
inputStream.close(); | |||||
} catch (IOException e) { | |||||
} | |||||
} | |||||
if (fos != null) { | |||||
try { | |||||
fos.close(); | |||||
} catch (IOException e) { | |||||
} | |||||
} | |||||
} | |||||
} | |||||
public void setWxConfigStorage(WxConfigStorage wxConfigProvider) { | public void setWxConfigStorage(WxConfigStorage wxConfigProvider) { | ||||
this.wxConfigStorage = wxConfigProvider; | this.wxConfigStorage = wxConfigProvider; | ||||
} | } | ||||
} | } |
@@ -1,6 +1,6 @@ | |||||
package chanjarster.weixin.bean; | package chanjarster.weixin.bean; | ||||
import chanjarster.weixin.util.WxGsonBuilder; | |||||
import chanjarster.weixin.util.json.WxGsonBuilder; | |||||
public class WxAccessToken { | public class WxAccessToken { | ||||
@@ -3,7 +3,7 @@ package chanjarster.weixin.bean; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import chanjarster.weixin.util.WxGsonBuilder; | |||||
import chanjarster.weixin.util.json.WxGsonBuilder; | |||||
/** | /** | ||||
* 客服消息 | * 客服消息 | ||||
@@ -5,7 +5,7 @@ import java.io.InputStreamReader; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import chanjarster.weixin.util.WxGsonBuilder; | |||||
import chanjarster.weixin.util.json.WxGsonBuilder; | |||||
/** | /** | ||||
* 公众号菜单 | * 公众号菜单 | ||||
@@ -9,8 +9,8 @@ import javax.xml.bind.annotation.XmlElement; | |||||
import javax.xml.bind.annotation.XmlRootElement; | import javax.xml.bind.annotation.XmlRootElement; | ||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; | import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; | ||||
import chanjarster.weixin.util.AdapterCDATA; | |||||
import chanjarster.weixin.util.XmlTransformer; | |||||
import chanjarster.weixin.util.xml.AdapterCDATA; | |||||
import chanjarster.weixin.util.xml.XmlTransformer; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -3,7 +3,7 @@ package chanjarster.weixin.bean.result; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import chanjarster.weixin.util.WxGsonBuilder; | |||||
import chanjarster.weixin.util.json.WxGsonBuilder; | |||||
/** | /** | ||||
* 微信错误码说明 | * 微信错误码说明 | ||||
@@ -1,6 +1,6 @@ | |||||
package chanjarster.weixin.bean.result; | package chanjarster.weixin.bean.result; | ||||
import chanjarster.weixin.util.WxGsonBuilder; | |||||
import chanjarster.weixin.util.json.WxGsonBuilder; | |||||
public class WxUploadResult { | public class WxUploadResult { | ||||
@@ -0,0 +1,47 @@ | |||||
package chanjarster.weixin.util.fs; | |||||
import java.io.File; | |||||
import java.io.FileOutputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
public class FileUtil { | |||||
/** | |||||
* 创建临时文件 | |||||
* @param inputStream | |||||
* @param name 文件名 | |||||
* @param ext 扩展名 | |||||
* @return | |||||
* @throws IOException | |||||
*/ | |||||
public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException { | |||||
FileOutputStream fos = null; | |||||
try { | |||||
File tmpFile = File.createTempFile(name, '.' + ext); | |||||
tmpFile.deleteOnExit(); | |||||
fos = new FileOutputStream(tmpFile); | |||||
int read = 0; | |||||
byte[] bytes = new byte[1024 * 100]; | |||||
while ((read = inputStream.read(bytes)) != -1) { | |||||
fos.write(bytes, 0, read); | |||||
} | |||||
fos.flush(); | |||||
return tmpFile; | |||||
} finally { | |||||
if (inputStream != null) { | |||||
try { | |||||
inputStream.close(); | |||||
} catch (IOException e) { | |||||
} | |||||
} | |||||
if (fos != null) { | |||||
try { | |||||
fos.close(); | |||||
} catch (IOException e) { | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,27 @@ | |||||
package chanjarster.weixin.util.http; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import org.apache.http.HttpEntity; | |||||
import org.apache.http.HttpResponse; | |||||
import org.apache.http.StatusLine; | |||||
import org.apache.http.client.HttpResponseException; | |||||
import org.apache.http.client.ResponseHandler; | |||||
import org.apache.http.util.EntityUtils; | |||||
public class InputStreamResponseHandler implements ResponseHandler<InputStream> { | |||||
public static final ResponseHandler<InputStream> INSTANCE = new InputStreamResponseHandler(); | |||||
public InputStream handleResponse(final HttpResponse response) throws HttpResponseException, IOException { | |||||
final StatusLine statusLine = response.getStatusLine(); | |||||
final HttpEntity entity = response.getEntity(); | |||||
if (statusLine.getStatusCode() >= 300) { | |||||
EntityUtils.consume(entity); | |||||
throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); | |||||
} | |||||
return entity == null ? null : entity.getContent(); | |||||
} | |||||
} |
@@ -0,0 +1,68 @@ | |||||
package chanjarster.weixin.util.http; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.regex.Matcher; | |||||
import java.util.regex.Pattern; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.apache.http.Header; | |||||
import org.apache.http.client.ClientProtocolException; | |||||
import org.apache.http.client.methods.CloseableHttpResponse; | |||||
import org.apache.http.client.methods.HttpGet; | |||||
import org.apache.http.entity.ContentType; | |||||
import chanjarster.weixin.bean.result.WxError; | |||||
import chanjarster.weixin.exception.WxErrorException; | |||||
import chanjarster.weixin.util.fs.FileUtil; | |||||
/** | |||||
* 下载媒体文件请求执行器,请求的参数是String, 返回的结果是String | |||||
* @author chanjarster | |||||
* | |||||
*/ | |||||
public class MediaDownloadRequestExecutor implements RequestExecutor<File, String> { | |||||
@Override | |||||
public File execute(String uri, String queryParam) throws WxErrorException, ClientProtocolException, IOException { | |||||
if (queryParam != null) { | |||||
if (uri.indexOf('?') == -1) { | |||||
uri += '?'; | |||||
} | |||||
uri += uri.endsWith("?") ? queryParam : '&' + queryParam; | |||||
} | |||||
HttpGet httpGet = new HttpGet(uri); | |||||
CloseableHttpResponse response = httpclient.execute(httpGet); | |||||
Header[] contentTypeHeader = response.getHeaders("Content-Type"); | |||||
if (contentTypeHeader != null && contentTypeHeader.length > 0) { | |||||
// 下载媒体文件出错 | |||||
if (ContentType.TEXT_PLAIN.getMimeType().equals(contentTypeHeader[0].getValue())) { | |||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | |||||
throw new WxErrorException(WxError.fromJson(responseContent)); | |||||
} | |||||
} | |||||
InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response); | |||||
// 视频文件不支持下载 | |||||
String fileName = getFileName(response); | |||||
if (StringUtils.isBlank(fileName)) { | |||||
return null; | |||||
} | |||||
String[] name_ext = fileName.split("\\."); | |||||
File localFile = FileUtil.createTmpFile(inputStream, name_ext[0], name_ext[1]); | |||||
return localFile; | |||||
} | |||||
protected String getFileName(CloseableHttpResponse response) { | |||||
Header[] contentDispositionHeader = response.getHeaders("Content-disposition"); | |||||
Pattern p = Pattern.compile(".*filename=\"(.*)\""); | |||||
Matcher m = p.matcher(contentDispositionHeader[0].getValue()); | |||||
m.matches(); | |||||
String fileName = m.group(1); | |||||
return fileName; | |||||
} | |||||
} |
@@ -0,0 +1,44 @@ | |||||
package chanjarster.weixin.util.http; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import org.apache.http.HttpEntity; | |||||
import org.apache.http.client.ClientProtocolException; | |||||
import org.apache.http.client.methods.CloseableHttpResponse; | |||||
import org.apache.http.client.methods.HttpPost; | |||||
import org.apache.http.entity.ContentType; | |||||
import org.apache.http.entity.mime.MultipartEntityBuilder; | |||||
import chanjarster.weixin.bean.result.WxError; | |||||
import chanjarster.weixin.bean.result.WxUploadResult; | |||||
import chanjarster.weixin.exception.WxErrorException; | |||||
/** | |||||
* 上传媒体文件请求执行器,请求的参数是File, 返回的结果是String | |||||
* @author chanjarster | |||||
* | |||||
*/ | |||||
public class MediaUploadRequestExecutor implements RequestExecutor<WxUploadResult, File> { | |||||
@Override | |||||
public WxUploadResult execute(String uri, File file) throws WxErrorException, ClientProtocolException, IOException { | |||||
HttpPost httpPost = new HttpPost(uri); | |||||
if (file != null) { | |||||
HttpEntity entity = MultipartEntityBuilder | |||||
.create() | |||||
.addBinaryBody("media", file) | |||||
.build(); | |||||
httpPost.setEntity(entity); | |||||
httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); | |||||
} | |||||
CloseableHttpResponse response = httpclient.execute(httpPost); | |||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | |||||
WxError error = WxError.fromJson(responseContent); | |||||
if (error.getErrcode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
return WxUploadResult.fromJson(responseContent); | |||||
} | |||||
} |
@@ -0,0 +1,24 @@ | |||||
package chanjarster.weixin.util.http; | |||||
import java.io.IOException; | |||||
import org.apache.http.client.ClientProtocolException; | |||||
import org.apache.http.impl.client.CloseableHttpClient; | |||||
import org.apache.http.impl.client.HttpClients; | |||||
import chanjarster.weixin.exception.WxErrorException; | |||||
/** | |||||
* http请求执行器 | |||||
* @author chanjarster | |||||
* | |||||
* @param <T> 返回值类型 | |||||
* @param <E> 请求参数类型 | |||||
*/ | |||||
public interface RequestExecutor<T, E> { | |||||
public static final CloseableHttpClient httpclient = HttpClients.createDefault(); | |||||
public T execute(String uri, E data) throws WxErrorException, ClientProtocolException, IOException; | |||||
} |
@@ -0,0 +1,37 @@ | |||||
package chanjarster.weixin.util.http; | |||||
import java.io.IOException; | |||||
import org.apache.http.client.ClientProtocolException; | |||||
import org.apache.http.client.methods.CloseableHttpResponse; | |||||
import org.apache.http.client.methods.HttpGet; | |||||
import chanjarster.weixin.bean.result.WxError; | |||||
import chanjarster.weixin.exception.WxErrorException; | |||||
/** | |||||
* 简单的GET请求执行器,请求的参数是String, 返回的结果也是String | |||||
* @author chanjarster | |||||
* | |||||
*/ | |||||
public class SimpleGetRequestExecutor implements RequestExecutor<String, String> { | |||||
@Override | |||||
public String execute(String uri, String queryParam) throws WxErrorException, ClientProtocolException, IOException { | |||||
if (queryParam != null) { | |||||
if (uri.indexOf('?') == -1) { | |||||
uri += '?'; | |||||
} | |||||
uri += uri.endsWith("?") ? queryParam : '&' + queryParam; | |||||
} | |||||
HttpGet httpGet = new HttpGet(uri); | |||||
CloseableHttpResponse response = httpclient.execute(httpGet); | |||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | |||||
WxError error = WxError.fromJson(responseContent); | |||||
if (error.getErrcode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
return responseContent; | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
package chanjarster.weixin.util.http; | |||||
import java.io.IOException; | |||||
import org.apache.http.Consts; | |||||
import org.apache.http.client.ClientProtocolException; | |||||
import org.apache.http.client.methods.CloseableHttpResponse; | |||||
import org.apache.http.client.methods.HttpPost; | |||||
import org.apache.http.entity.StringEntity; | |||||
import chanjarster.weixin.bean.result.WxError; | |||||
import chanjarster.weixin.exception.WxErrorException; | |||||
/** | |||||
* 简单的POST请求执行器,请求的参数是String, 返回的结果也是String | |||||
* @author chanjarster | |||||
* | |||||
*/ | |||||
public class SimplePostRequestExecutor implements RequestExecutor<String, String> { | |||||
@Override | |||||
public String execute(String uri, String postEntity) throws WxErrorException, ClientProtocolException, IOException { | |||||
HttpPost httpPost = new HttpPost(uri); | |||||
if (postEntity != null) { | |||||
StringEntity entity = new StringEntity(postEntity, Consts.UTF_8); | |||||
httpPost.setEntity(entity); | |||||
} | |||||
CloseableHttpResponse response = httpclient.execute(httpPost); | |||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | |||||
WxError error = WxError.fromJson(responseContent); | |||||
if (error.getErrcode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
return responseContent; | |||||
} | |||||
} |
@@ -1,4 +1,4 @@ | |||||
package chanjarster.weixin.util; | |||||
package chanjarster.weixin.util.http; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
@@ -6,7 +6,7 @@ | |||||
* arose from modification of the original source, or other redistribution of this source | * arose from modification of the original source, or other redistribution of this source | ||||
* is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | ||||
*/ | */ | ||||
package chanjarster.weixin.util; | |||||
package chanjarster.weixin.util.json; | |||||
import com.google.gson.JsonElement; | import com.google.gson.JsonElement; |
@@ -6,7 +6,7 @@ | |||||
* arose from modification of the original source, or other redistribution of this source | * arose from modification of the original source, or other redistribution of this source | ||||
* is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | ||||
*/ | */ | ||||
package chanjarster.weixin.util; | |||||
package chanjarster.weixin.util.json; | |||||
import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
@@ -1,4 +1,4 @@ | |||||
package chanjarster.weixin.util; | |||||
package chanjarster.weixin.util.json; | |||||
import chanjarster.weixin.bean.WxCustomMessage; | import chanjarster.weixin.bean.WxCustomMessage; | ||||
import chanjarster.weixin.bean.WxMenu; | import chanjarster.weixin.bean.WxMenu; |
@@ -6,7 +6,7 @@ | |||||
* arose from modification of the original source, or other redistribution of this source | * arose from modification of the original source, or other redistribution of this source | ||||
* is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | ||||
*/ | */ | ||||
package chanjarster.weixin.util; | |||||
package chanjarster.weixin.util.json; | |||||
import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
@@ -1,4 +1,4 @@ | |||||
package chanjarster.weixin.util; | |||||
package chanjarster.weixin.util.xml; | |||||
import javax.xml.bind.annotation.adapters.XmlAdapter; | import javax.xml.bind.annotation.adapters.XmlAdapter; | ||||
@@ -1,4 +1,4 @@ | |||||
package chanjarster.weixin.util; | |||||
package chanjarster.weixin.util.xml; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; |
@@ -2,6 +2,8 @@ package chanjarster.weixin.api; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import javax.xml.bind.JAXBException; | import javax.xml.bind.JAXBException; | ||||
import javax.xml.bind.annotation.XmlAccessType; | import javax.xml.bind.annotation.XmlAccessType; | ||||
@@ -19,14 +21,17 @@ import chanjarster.weixin.bean.WxMenu; | |||||
import chanjarster.weixin.bean.WxMenu.WxMenuButton; | import chanjarster.weixin.bean.WxMenu.WxMenuButton; | ||||
import chanjarster.weixin.bean.result.WxUploadResult; | import chanjarster.weixin.bean.result.WxUploadResult; | ||||
import chanjarster.weixin.exception.WxErrorException; | import chanjarster.weixin.exception.WxErrorException; | ||||
import chanjarster.weixin.util.XmlTransformer; | |||||
import chanjarster.weixin.util.xml.XmlTransformer; | |||||
public class WxServiceTest { | public class WxServiceTest { | ||||
private WxServiceImpl wxService; | private WxServiceImpl wxService; | ||||
private List<String> media_ids = new ArrayList<String>(); | |||||
@BeforeTest | @BeforeTest | ||||
public void prepare() throws JAXBException { | public void prepare() throws JAXBException { | ||||
this.wxService = null; | |||||
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | ||||
WxXmlConfigStorage config = XmlTransformer.fromXml(WxXmlConfigStorage.class, is1); | WxXmlConfigStorage config = XmlTransformer.fromXml(WxXmlConfigStorage.class, is1); | ||||
this.wxService = new WxServiceImpl(); | this.wxService = new WxServiceImpl(); | ||||
@@ -66,32 +71,51 @@ public class WxServiceTest { | |||||
Assert.assertNotNull(wxService.getMenu()); | Assert.assertNotNull(wxService.getMenu()); | ||||
} | } | ||||
@Test(dependsOnMethods = { "testRefreshAccessToken", "testGetMenu" }, enabled = true) | |||||
@Test(dependsOnMethods = { "testRefreshAccessToken", "testGetMenu"}, enabled = true) | |||||
public void testDeleteMenu() throws WxErrorException { | public void testDeleteMenu() throws WxErrorException { | ||||
wxService.deleteMenu(); | wxService.deleteMenu(); | ||||
} | } | ||||
@Test(dependsOnMethods = { "testRefreshAccessToken" }, dataProvider="uploadFiles", enabled = true) | |||||
@Test(dependsOnMethods = { "testRefreshAccessToken" }, dataProvider="uploadMedia", enabled = true) | |||||
public void testUploadMedia1(String mediaType, String fileType, String fileName) throws WxErrorException, IOException { | public void testUploadMedia1(String mediaType, String fileType, String fileName) throws WxErrorException, IOException { | ||||
InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName); | InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName); | ||||
WxUploadResult res = wxService.uploadMedia(mediaType, fileType, inputStream); | WxUploadResult res = wxService.uploadMedia(mediaType, fileType, inputStream); | ||||
Assert.assertNotNull(res.getType()); | Assert.assertNotNull(res.getType()); | ||||
Assert.assertNotNull(res.getCreated_at()); | Assert.assertNotNull(res.getCreated_at()); | ||||
Assert.assertTrue(res.getMedia_id() != null || res.getThumb_media_id() != null); | Assert.assertTrue(res.getMedia_id() != null || res.getThumb_media_id() != null); | ||||
if (res.getMedia_id() != null) { | |||||
media_ids.add(res.getMedia_id()); | |||||
} | |||||
if (res.getThumb_media_id() != null) { | |||||
media_ids.add(res.getThumb_media_id()); | |||||
} | |||||
} | } | ||||
@DataProvider | @DataProvider | ||||
public Object[][] uploadFiles() { | |||||
public Object[][] uploadMedia() { | |||||
return new Object[][] { | return new Object[][] { | ||||
new Object[] { WxConsts.MEDIA_IMAGE, WxConsts.FILE_PNG, "mm.png" }, | |||||
new Object[] { WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, "mm.jpeg" }, | new Object[] { WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, "mm.jpeg" }, | ||||
new Object[] { WxConsts.MEDIA_VOICE, WxConsts.FILE_MP3, "mm.mp3" }, | new Object[] { WxConsts.MEDIA_VOICE, WxConsts.FILE_MP3, "mm.mp3" }, | ||||
new Object[] { WxConsts.MEDIA_VIDEO, WxConsts.FILE_MP4, "mm.mp4" }, | new Object[] { WxConsts.MEDIA_VIDEO, WxConsts.FILE_MP4, "mm.mp4" }, | ||||
new Object[] { WxConsts.MEDIA_THUMB, WxConsts.FILE_PNG, "mm.png" }, | |||||
new Object[] { WxConsts.MEDIA_THUMB, WxConsts.FILE_JPG, "mm.jpeg" } | new Object[] { WxConsts.MEDIA_THUMB, WxConsts.FILE_JPG, "mm.jpeg" } | ||||
}; | }; | ||||
} | } | ||||
@Test(dependsOnMethods = { "testUploadMedia1" }, dataProvider="downloadMedia") | |||||
public void testDownloadMedia(String media_id) throws WxErrorException { | |||||
wxService.downloadMedia(media_id); | |||||
} | |||||
@DataProvider | |||||
public Object[][] downloadMedia() { | |||||
Object[][] params = new Object[this.media_ids.size()][]; | |||||
for (int i = 0; i < this.media_ids.size(); i++) { | |||||
params[i] = new Object[] { this.media_ids.get(i) }; | |||||
} | |||||
return params; | |||||
} | |||||
@Test(enabled = true) | @Test(enabled = true) | ||||
public void testCheckSignature() throws WxErrorException { | public void testCheckSignature() throws WxErrorException { | ||||
String timestamp = "23234235423246"; | String timestamp = "23234235423246"; | ||||