From 71215bbe79ae66121e35360ff154422f14f78be1 Mon Sep 17 00:00:00 2001 From: Daniel Qian Date: Sun, 24 Aug 2014 14:18:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=9A=E5=AA=92=E4=BD=93?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8B=E8=BD=BD=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/chanjarster/weixin/api/WxConsts.java | 1 - .../chanjarster/weixin/api/WxService.java | 19 ++- .../chanjarster/weixin/api/WxServiceImpl.java | 135 ++++++------------ .../weixin/bean/WxAccessToken.java | 2 +- .../weixin/bean/WxCustomMessage.java | 2 +- .../java/chanjarster/weixin/bean/WxMenu.java | 2 +- .../chanjarster/weixin/bean/WxXmlMessage.java | 4 +- .../weixin/bean/result/WxError.java | 2 +- .../weixin/bean/result/WxUploadResult.java | 2 +- .../chanjarster/weixin/util/fs/FileUtil.java | 47 ++++++ .../util/http/InputStreamResponseHandler.java | 27 ++++ .../http/MediaDownloadRequestExecutor.java | 68 +++++++++ .../util/http/MediaUploadRequestExecutor.java | 44 ++++++ .../weixin/util/http/RequestExecutor.java | 24 ++++ .../util/http/SimpleGetRequestExecutor.java | 37 +++++ .../util/http/SimplePostRequestExecutor.java | 37 +++++ .../util/{ => http}/Utf8ResponseHandler.java | 2 +- .../weixin/util/{ => json}/GsonHelper.java | 2 +- .../WxCustomMessageGsonAdapter.java | 2 +- .../weixin/util/{ => json}/WxGsonBuilder.java | 2 +- .../util/{ => json}/WxMenuGsonAdapter.java | 2 +- .../weixin/util/{ => xml}/AdapterCDATA.java | 2 +- .../weixin/util/{ => xml}/XmlTransformer.java | 2 +- .../chanjarster/weixin/api/WxServiceTest.java | 36 ++++- src/test/resources/mm.png | Bin 37395 -> 0 bytes 25 files changed, 386 insertions(+), 117 deletions(-) create mode 100644 src/main/java/chanjarster/weixin/util/fs/FileUtil.java create mode 100644 src/main/java/chanjarster/weixin/util/http/InputStreamResponseHandler.java create mode 100644 src/main/java/chanjarster/weixin/util/http/MediaDownloadRequestExecutor.java create mode 100644 src/main/java/chanjarster/weixin/util/http/MediaUploadRequestExecutor.java create mode 100644 src/main/java/chanjarster/weixin/util/http/RequestExecutor.java create mode 100644 src/main/java/chanjarster/weixin/util/http/SimpleGetRequestExecutor.java create mode 100644 src/main/java/chanjarster/weixin/util/http/SimplePostRequestExecutor.java rename src/main/java/chanjarster/weixin/util/{ => http}/Utf8ResponseHandler.java (96%) rename src/main/java/chanjarster/weixin/util/{ => json}/GsonHelper.java (98%) rename src/main/java/chanjarster/weixin/util/{ => json}/WxCustomMessageGsonAdapter.java (98%) rename src/main/java/chanjarster/weixin/util/{ => json}/WxGsonBuilder.java (93%) rename src/main/java/chanjarster/weixin/util/{ => json}/WxMenuGsonAdapter.java (98%) rename src/main/java/chanjarster/weixin/util/{ => xml}/AdapterCDATA.java (92%) rename src/main/java/chanjarster/weixin/util/{ => xml}/XmlTransformer.java (98%) delete mode 100644 src/test/resources/mm.png diff --git a/src/main/java/chanjarster/weixin/api/WxConsts.java b/src/main/java/chanjarster/weixin/api/WxConsts.java index 0f4fbb8e..d2ae9050 100644 --- a/src/main/java/chanjarster/weixin/api/WxConsts.java +++ b/src/main/java/chanjarster/weixin/api/WxConsts.java @@ -26,7 +26,6 @@ public class WxConsts { public static final String MEDIA_THUMB = "thumb"; 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_ARM = "arm"; public static final String FILE_MP4 = "mp4"; diff --git a/src/main/java/chanjarster/weixin/api/WxService.java b/src/main/java/chanjarster/weixin/api/WxService.java index b39fbf6c..fe37ed15 100644 --- a/src/main/java/chanjarster/weixin/api/WxService.java +++ b/src/main/java/chanjarster/weixin/api/WxService.java @@ -44,6 +44,7 @@ public interface WxService { /** *
    * 上传多媒体文件
+   * 
    * 上传的多媒体文件有格式和大小限制,如下:
    *   图片(image): 1M,支持JPG格式
    *   语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
@@ -67,6 +68,18 @@ public interface WxService {
    */
   public WxUploadResult uploadMedia(String mediaType, File file) throws WxErrorException;
   
+  /**
+   * 
+   * 下载多媒体文件
+   * 
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
+   * 
+ * @params media_id + * @return 保存到本地的临时文件 + * @throws WxErrorException + */ + public File downloadMedia(String media_id) throws WxErrorException; + /** *
    * 发送客服消息
@@ -75,7 +88,7 @@ public interface WxService {
    * @param message
    * @throws WxErrorException
    */
-  public String sendCustomMessage(WxCustomMessage message) throws WxErrorException;
+  public void sendCustomMessage(WxCustomMessage message) throws WxErrorException;
   
   /**
    * 
@@ -85,7 +98,7 @@ public interface WxService {
    * @param menu
    * @throws WxErrorException
    */
-  public String createMenu(WxMenu menu) throws WxErrorException;
+  public void createMenu(WxMenu menu) throws WxErrorException;
   
   /**
    * 
@@ -94,7 +107,7 @@ public interface WxService {
    * 
* @throws WxErrorException */ - public String deleteMenu() throws WxErrorException; + public void deleteMenu() throws WxErrorException; /** *
diff --git a/src/main/java/chanjarster/weixin/api/WxServiceImpl.java b/src/main/java/chanjarster/weixin/api/WxServiceImpl.java
index 51d7421c..f6e4b19f 100644
--- a/src/main/java/chanjarster/weixin/api/WxServiceImpl.java
+++ b/src/main/java/chanjarster/weixin/api/WxServiceImpl.java
@@ -1,7 +1,6 @@
 package chanjarster.weixin.api;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.MessageDigest;
@@ -10,15 +9,9 @@ import java.util.UUID;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 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.methods.CloseableHttpResponse;
 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.CloseableHttpClient;
 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.WxUploadResult;
 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 {
 
@@ -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',
                        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
     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";
-    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";
-    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";
-    return get(url, null);
+    execute(new SimpleGetRequestExecutor(), url, null);
   }
 
   public WxMenu getMenu() throws WxErrorException {
     String url = "https://api.weixin.qq.com/cgi-bin/menu/get";
     try {
-      String resultContent = get(url, null);
+      String resultContent = execute(new SimpleGetRequestExecutor(), url, null);
       return WxMenu.fromJson(resultContent);
     } catch (WxErrorException e) {
       // 46003 不存在的菜单数据
@@ -140,30 +138,28 @@ public class WxServiceImpl implements WxService {
   }
 
   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 {
     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过期时才去刷新,然后重新执行请求,而不是全局定时请求
-   * @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 execute(RequestExecutor executor, String uri, E data) throws WxErrorException {
     if (StringUtils.isBlank(wxConfigStorage.getAccessToken())) {
       refreshAccessToken();
     }
@@ -173,38 +169,9 @@ public class WxServiceImpl implements WxService {
     uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken;
     
     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
        * 40001 获取access_token时AppSecret错误,或者access_token无效
@@ -212,12 +179,24 @@ public class WxServiceImpl implements WxService {
        */
       if (error.getErrcode() == 42001 || error.getErrcode() == 40001) {
         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) {
         throw new WxErrorException(error);
       }
-      return resultContent;
+      return null;
     } catch (ClientProtocolException e) {
       throw new RuntimeException(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) {
     this.wxConfigStorage = wxConfigProvider;
   }
 
-
 }
diff --git a/src/main/java/chanjarster/weixin/bean/WxAccessToken.java b/src/main/java/chanjarster/weixin/bean/WxAccessToken.java
index 90b357d0..8c02f538 100644
--- a/src/main/java/chanjarster/weixin/bean/WxAccessToken.java
+++ b/src/main/java/chanjarster/weixin/bean/WxAccessToken.java
@@ -1,6 +1,6 @@
 package chanjarster.weixin.bean;
 
-import chanjarster.weixin.util.WxGsonBuilder;
+import chanjarster.weixin.util.json.WxGsonBuilder;
 
 public class WxAccessToken {
 
diff --git a/src/main/java/chanjarster/weixin/bean/WxCustomMessage.java b/src/main/java/chanjarster/weixin/bean/WxCustomMessage.java
index 9b41f8bf..2ca24e67 100644
--- a/src/main/java/chanjarster/weixin/bean/WxCustomMessage.java
+++ b/src/main/java/chanjarster/weixin/bean/WxCustomMessage.java
@@ -3,7 +3,7 @@ package chanjarster.weixin.bean;
 import java.util.ArrayList;
 import java.util.List;
 
-import chanjarster.weixin.util.WxGsonBuilder;
+import chanjarster.weixin.util.json.WxGsonBuilder;
 
 /**
  * 客服消息
diff --git a/src/main/java/chanjarster/weixin/bean/WxMenu.java b/src/main/java/chanjarster/weixin/bean/WxMenu.java
index a1662c7d..be79386c 100644
--- a/src/main/java/chanjarster/weixin/bean/WxMenu.java
+++ b/src/main/java/chanjarster/weixin/bean/WxMenu.java
@@ -5,7 +5,7 @@ import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.List;
 
-import chanjarster.weixin.util.WxGsonBuilder;
+import chanjarster.weixin.util.json.WxGsonBuilder;
 
 /**
  * 公众号菜单
diff --git a/src/main/java/chanjarster/weixin/bean/WxXmlMessage.java b/src/main/java/chanjarster/weixin/bean/WxXmlMessage.java
index a89f649e..71650567 100644
--- a/src/main/java/chanjarster/weixin/bean/WxXmlMessage.java
+++ b/src/main/java/chanjarster/weixin/bean/WxXmlMessage.java
@@ -9,8 +9,8 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 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;
 
 /**
  * 
diff --git a/src/main/java/chanjarster/weixin/bean/result/WxError.java b/src/main/java/chanjarster/weixin/bean/result/WxError.java
index 051a777e..69a4ddef 100644
--- a/src/main/java/chanjarster/weixin/bean/result/WxError.java
+++ b/src/main/java/chanjarster/weixin/bean/result/WxError.java
@@ -3,7 +3,7 @@ package chanjarster.weixin.bean.result;
 import java.util.HashMap;
 import java.util.Map;
 
-import chanjarster.weixin.util.WxGsonBuilder;
+import chanjarster.weixin.util.json.WxGsonBuilder;
 
 /**
  * 微信错误码说明
diff --git a/src/main/java/chanjarster/weixin/bean/result/WxUploadResult.java b/src/main/java/chanjarster/weixin/bean/result/WxUploadResult.java
index cf605fe9..98bee3e3 100644
--- a/src/main/java/chanjarster/weixin/bean/result/WxUploadResult.java
+++ b/src/main/java/chanjarster/weixin/bean/result/WxUploadResult.java
@@ -1,6 +1,6 @@
 package chanjarster.weixin.bean.result;
 
-import chanjarster.weixin.util.WxGsonBuilder;
+import chanjarster.weixin.util.json.WxGsonBuilder;
 
 public class WxUploadResult {
 
diff --git a/src/main/java/chanjarster/weixin/util/fs/FileUtil.java b/src/main/java/chanjarster/weixin/util/fs/FileUtil.java
new file mode 100644
index 00000000..3faeb4dd
--- /dev/null
+++ b/src/main/java/chanjarster/weixin/util/fs/FileUtil.java
@@ -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) {
+        }
+      }
+    }
+  }
+  
+}
diff --git a/src/main/java/chanjarster/weixin/util/http/InputStreamResponseHandler.java b/src/main/java/chanjarster/weixin/util/http/InputStreamResponseHandler.java
new file mode 100644
index 00000000..37d57790
--- /dev/null
+++ b/src/main/java/chanjarster/weixin/util/http/InputStreamResponseHandler.java
@@ -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 {
+
+  public static final ResponseHandler 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();
+  }
+
+}
diff --git a/src/main/java/chanjarster/weixin/util/http/MediaDownloadRequestExecutor.java b/src/main/java/chanjarster/weixin/util/http/MediaDownloadRequestExecutor.java
new file mode 100644
index 00000000..46e563d5
--- /dev/null
+++ b/src/main/java/chanjarster/weixin/util/http/MediaDownloadRequestExecutor.java
@@ -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 {
+
+  @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;
+  }
+  
+}
diff --git a/src/main/java/chanjarster/weixin/util/http/MediaUploadRequestExecutor.java b/src/main/java/chanjarster/weixin/util/http/MediaUploadRequestExecutor.java
new file mode 100644
index 00000000..46287ab1
--- /dev/null
+++ b/src/main/java/chanjarster/weixin/util/http/MediaUploadRequestExecutor.java
@@ -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 {
+
+  @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);
+  }
+
+}
diff --git a/src/main/java/chanjarster/weixin/util/http/RequestExecutor.java b/src/main/java/chanjarster/weixin/util/http/RequestExecutor.java
new file mode 100644
index 00000000..e80bd61c
--- /dev/null
+++ b/src/main/java/chanjarster/weixin/util/http/RequestExecutor.java
@@ -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  返回值类型
+ * @param  请求参数类型
+ */
+public interface RequestExecutor {
+
+  public static final CloseableHttpClient httpclient = HttpClients.createDefault();
+
+  public T execute(String uri, E data) throws WxErrorException, ClientProtocolException, IOException;
+  
+}
diff --git a/src/main/java/chanjarster/weixin/util/http/SimpleGetRequestExecutor.java b/src/main/java/chanjarster/weixin/util/http/SimpleGetRequestExecutor.java
new file mode 100644
index 00000000..91ac1e58
--- /dev/null
+++ b/src/main/java/chanjarster/weixin/util/http/SimpleGetRequestExecutor.java
@@ -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 {
+
+  @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;
+  }
+
+}
diff --git a/src/main/java/chanjarster/weixin/util/http/SimplePostRequestExecutor.java b/src/main/java/chanjarster/weixin/util/http/SimplePostRequestExecutor.java
new file mode 100644
index 00000000..4f3c5bc4
--- /dev/null
+++ b/src/main/java/chanjarster/weixin/util/http/SimplePostRequestExecutor.java
@@ -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 {
+
+  @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;
+  }
+
+}
diff --git a/src/main/java/chanjarster/weixin/util/Utf8ResponseHandler.java b/src/main/java/chanjarster/weixin/util/http/Utf8ResponseHandler.java
similarity index 96%
rename from src/main/java/chanjarster/weixin/util/Utf8ResponseHandler.java
rename to src/main/java/chanjarster/weixin/util/http/Utf8ResponseHandler.java
index 9884425d..384205e5 100644
--- a/src/main/java/chanjarster/weixin/util/Utf8ResponseHandler.java
+++ b/src/main/java/chanjarster/weixin/util/http/Utf8ResponseHandler.java
@@ -1,4 +1,4 @@
-package chanjarster.weixin.util;
+package chanjarster.weixin.util.http;
 
 import java.io.IOException;
 
diff --git a/src/main/java/chanjarster/weixin/util/GsonHelper.java b/src/main/java/chanjarster/weixin/util/json/GsonHelper.java
similarity index 98%
rename from src/main/java/chanjarster/weixin/util/GsonHelper.java
rename to src/main/java/chanjarster/weixin/util/json/GsonHelper.java
index 0b138a5e..7487b9fc 100644
--- a/src/main/java/chanjarster/weixin/util/GsonHelper.java
+++ b/src/main/java/chanjarster/weixin/util/json/GsonHelper.java
@@ -6,7 +6,7 @@
  * 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.
  */
-package chanjarster.weixin.util;
+package chanjarster.weixin.util.json;
 
 
 import com.google.gson.JsonElement;
diff --git a/src/main/java/chanjarster/weixin/util/WxCustomMessageGsonAdapter.java b/src/main/java/chanjarster/weixin/util/json/WxCustomMessageGsonAdapter.java
similarity index 98%
rename from src/main/java/chanjarster/weixin/util/WxCustomMessageGsonAdapter.java
rename to src/main/java/chanjarster/weixin/util/json/WxCustomMessageGsonAdapter.java
index b6f0a972..cfe9c289 100644
--- a/src/main/java/chanjarster/weixin/util/WxCustomMessageGsonAdapter.java
+++ b/src/main/java/chanjarster/weixin/util/json/WxCustomMessageGsonAdapter.java
@@ -6,7 +6,7 @@
  * 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.
  */
-package chanjarster.weixin.util;
+package chanjarster.weixin.util.json;
 
 import java.lang.reflect.Type;
 
diff --git a/src/main/java/chanjarster/weixin/util/WxGsonBuilder.java b/src/main/java/chanjarster/weixin/util/json/WxGsonBuilder.java
similarity index 93%
rename from src/main/java/chanjarster/weixin/util/WxGsonBuilder.java
rename to src/main/java/chanjarster/weixin/util/json/WxGsonBuilder.java
index 60e17b48..c77fafc6 100644
--- a/src/main/java/chanjarster/weixin/util/WxGsonBuilder.java
+++ b/src/main/java/chanjarster/weixin/util/json/WxGsonBuilder.java
@@ -1,4 +1,4 @@
-package chanjarster.weixin.util;
+package chanjarster.weixin.util.json;
 
 import chanjarster.weixin.bean.WxCustomMessage;
 import chanjarster.weixin.bean.WxMenu;
diff --git a/src/main/java/chanjarster/weixin/util/WxMenuGsonAdapter.java b/src/main/java/chanjarster/weixin/util/json/WxMenuGsonAdapter.java
similarity index 98%
rename from src/main/java/chanjarster/weixin/util/WxMenuGsonAdapter.java
rename to src/main/java/chanjarster/weixin/util/json/WxMenuGsonAdapter.java
index 020fa2cf..78b6cc74 100644
--- a/src/main/java/chanjarster/weixin/util/WxMenuGsonAdapter.java
+++ b/src/main/java/chanjarster/weixin/util/json/WxMenuGsonAdapter.java
@@ -6,7 +6,7 @@
  * 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.
  */
-package chanjarster.weixin.util;
+package chanjarster.weixin.util.json;
 
 import java.lang.reflect.Type;
 
diff --git a/src/main/java/chanjarster/weixin/util/AdapterCDATA.java b/src/main/java/chanjarster/weixin/util/xml/AdapterCDATA.java
similarity index 92%
rename from src/main/java/chanjarster/weixin/util/AdapterCDATA.java
rename to src/main/java/chanjarster/weixin/util/xml/AdapterCDATA.java
index 581fcde4..84b96fdf 100644
--- a/src/main/java/chanjarster/weixin/util/AdapterCDATA.java
+++ b/src/main/java/chanjarster/weixin/util/xml/AdapterCDATA.java
@@ -1,4 +1,4 @@
-package chanjarster.weixin.util;
+package chanjarster.weixin.util.xml;
 
 import javax.xml.bind.annotation.adapters.XmlAdapter;
 
diff --git a/src/main/java/chanjarster/weixin/util/XmlTransformer.java b/src/main/java/chanjarster/weixin/util/xml/XmlTransformer.java
similarity index 98%
rename from src/main/java/chanjarster/weixin/util/XmlTransformer.java
rename to src/main/java/chanjarster/weixin/util/xml/XmlTransformer.java
index 7f52efa7..081fc67f 100644
--- a/src/main/java/chanjarster/weixin/util/XmlTransformer.java
+++ b/src/main/java/chanjarster/weixin/util/xml/XmlTransformer.java
@@ -1,4 +1,4 @@
-package chanjarster.weixin.util;
+package chanjarster.weixin.util.xml;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/src/test/java/chanjarster/weixin/api/WxServiceTest.java b/src/test/java/chanjarster/weixin/api/WxServiceTest.java
index 65feaab5..4649a161 100644
--- a/src/test/java/chanjarster/weixin/api/WxServiceTest.java
+++ b/src/test/java/chanjarster/weixin/api/WxServiceTest.java
@@ -2,6 +2,8 @@ package chanjarster.weixin.api;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
 
 import javax.xml.bind.JAXBException;
 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.result.WxUploadResult;
 import chanjarster.weixin.exception.WxErrorException;
-import chanjarster.weixin.util.XmlTransformer;
+import chanjarster.weixin.util.xml.XmlTransformer;
 
 public class WxServiceTest {
 
   private WxServiceImpl wxService;
   
+  private List media_ids = new ArrayList();
+  
   @BeforeTest
   public void prepare() throws JAXBException {
+    this.wxService = null;
     InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml");
     WxXmlConfigStorage config = XmlTransformer.fromXml(WxXmlConfigStorage.class, is1);
     this.wxService = new WxServiceImpl();
@@ -66,32 +71,51 @@ public class WxServiceTest {
     Assert.assertNotNull(wxService.getMenu());
   }
   
-  @Test(dependsOnMethods = { "testRefreshAccessToken", "testGetMenu" }, enabled = true)
+  @Test(dependsOnMethods = { "testRefreshAccessToken", "testGetMenu"}, enabled = true)
   public void testDeleteMenu() throws WxErrorException {
     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 {
     InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName);
     WxUploadResult res = wxService.uploadMedia(mediaType, fileType, inputStream);
     Assert.assertNotNull(res.getType());
     Assert.assertNotNull(res.getCreated_at());
     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
-  public Object[][] uploadFiles() {
+  public Object[][] uploadMedia() {
     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_VOICE, WxConsts.FILE_MP3, "mm.mp3" },
         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" }
     };
   }
   
+  @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)
   public void testCheckSignature() throws WxErrorException {
     String timestamp = "23234235423246";
diff --git a/src/test/resources/mm.png b/src/test/resources/mm.png
deleted file mode 100644
index a060d4434e4272f52031c03ba0214a5a646fc034..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 37395
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
Hu0mjf{ftIu