diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java new file mode 100644 index 00000000..a4b18643 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java @@ -0,0 +1,182 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.mp.bean.*; +import me.chanjar.weixin.mp.bean.result.*; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by Binary Wang on 2016/7/21. + * 素材管理的相关接口,包括媒体管理的接口, + * 即以https://api.weixin.qq.com/cgi-bin/material + * 和 https://api.weixin.qq.com/cgi-bin/media开头的接口 + */ +public interface WxMpMaterialService { + + /** + * 新增临时素材 + * @see #mediaUpload(String, String, InputStream) + * @param mediaType + * @param file + * @throws WxErrorException + */ + public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; + + /** + *
+   * 获取临时素材
+   * 本接口即为原“下载多媒体文件”接口。
+   * 根据微信文档,视频文件下载不了,会返回null
+   * 详情请见: 获取临时素材
+   * 
+ * @param media_id + * @return 保存到本地的临时文件 + * @throws WxErrorException + */ + public File mediaDownload(String media_id) throws WxErrorException; + + /** + *
+   * 上传图文消息内的图片获取URL
+   * 详情请见:http://mp.weixin.qq.com/wiki/15/40b6865b893947b764e2de8e4a1fb55f.html#.E4.B8.8A.E4.BC.A0.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF.E5.86.85.E7.9A.84.E5.9B.BE.E7.89.87.E8.8E.B7.E5.8F.96URL.E3.80.90.E8.AE.A2.E9.98.85.E5.8F.B7.E4.B8.8E.E6.9C.8D.E5.8A.A1.E5.8F.B7.E8.AE.A4.E8.AF.81.E5.90.8E.E5.9D.87.E5.8F.AF.E7.94.A8.E3.80.91
+   * 
+ * @param file + * @return WxMediaImgUploadResult 返回图片url + * @throws WxErrorException + */ + public WxMediaImgUploadResult mediaImgUpload(File file) throws WxErrorException; + + /** + *
+   * 新增临时素材
+   * 本接口即为原“上传多媒体文件”接口。
+   *
+   * 上传的多媒体文件有格式和大小限制,如下:
+   *   图片(image): 1M,支持JPG格式
+   *   语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
+   *   视频(video):10MB,支持MP4格式
+   *   缩略图(thumb):64KB,支持JPG格式
+   *
+   * 详情请见: 新增临时素材
+   * 
+ * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param fileType 文件类型,请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param inputStream 输入流 + * @throws WxErrorException + */ + public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException; + + /** + *
+   * 上传非图文永久素材
+   *
+   * 上传的多媒体文件有格式和大小限制,如下:
+   *   图片(image): 图片大小不超过2M,支持bmp/png/jpeg/jpg/gif格式
+   *   语音(voice):语音大小不超过5M,长度不超过60秒,支持mp3/wma/wav/amr格式
+   *   视频(video):在上传视频素材时需要POST另一个表单,id为description,包含素材的描述信息,内容格式为JSON
+   *   缩略图(thumb):文档未说明
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki/14/7e6c03263063f4813141c3e17dd4350a.html
+   * 
+ * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param material 上传的素材, 请看{@link me.chanjar.weixin.mp.bean.WxMpMaterial} + */ + public WxMpMaterialUploadResult materialFileUpload(String mediaType, WxMpMaterial material) throws WxErrorException; + + /** + *
+   * 上传永久图文素材
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki/14/7e6c03263063f4813141c3e17dd4350a.html
+   * 
+ * @param news 上传的图文消息, 请看{@link me.chanjar.weixin.mp.bean.WxMpMaterialNews} + */ + public WxMpMaterialUploadResult materialNewsUpload(WxMpMaterialNews news) throws WxErrorException; + + /** + *
+   * 下载声音或者图片永久素材
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki/4/b3546879f07623cb30df9ca0e420a5d0.html
+   * 
+ * @param media_id 永久素材的id + */ + public InputStream materialImageOrVoiceDownload(String media_id) throws WxErrorException; + + /** + *
+   * 获取视频永久素材的信息和下载地址
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki/4/b3546879f07623cb30df9ca0e420a5d0.html
+   * 
+ * @param media_id 永久素材的id + */ + public WxMpMaterialVideoInfoResult materialVideoInfo(String media_id) throws WxErrorException; + + /** + *
+   * 获取图文永久素材的信息
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki/4/b3546879f07623cb30df9ca0e420a5d0.html
+   * 
+ * @param media_id 永久素材的id + */ + public WxMpMaterialNews materialNewsInfo(String media_id) throws WxErrorException; + + /** + *
+   * 更新图文永久素材
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki/4/19a59cba020d506e767360ca1be29450.html
+   * 
+ * @param wxMpMaterialArticleUpdate 用来更新图文素材的bean, 请看{@link me.chanjar.weixin.mp.bean.WxMpMaterialArticleUpdate} + */ + public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate) throws WxErrorException; + + /** + *
+   * 删除永久素材
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki/5/e66f61c303db51a6c0f90f46b15af5f5.html
+   * 
+ * @param media_id 永久素材的id + */ + public boolean materialDelete(String media_id) throws WxErrorException; + + /** + *
+   * 获取各类素材总数
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki/16/8cc64f8c189674b421bee3ed403993b8.html
+   * 
+ */ + public WxMpMaterialCountResult materialCount() throws WxErrorException; + + /** + *
+   * 分页获取图文素材列表
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki/12/2108cd7aafff7f388f41f37efa710204.html
+   * 
+ * @param offset 从全部素材的该偏移位置开始返回,0表示从第一个素材 返回 + * @param count 返回素材的数量,取值在1到20之间 + */ + public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count) throws WxErrorException; + + /** + *
+   * 分页获取其他媒体素材列表
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki/12/2108cd7aafff7f388f41f37efa710204.html
+   * 
+ * @param type 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param offset 从全部素材的该偏移位置开始返回,0表示从第一个素材 返回 + * @param count 返回素材的数量,取值在1到20之间 + */ + public WxMpMaterialFileBatchGetResult materialFileBatchGet(String type, int offset, int count) throws WxErrorException; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index 7e9e0d92..3779c3c7 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -110,157 +110,6 @@ public interface WxMpService { */ public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; - /** - *
-   * 新增临时素材
-   * 本接口即为原“上传多媒体文件”接口。
-   *
-   * 上传的多媒体文件有格式和大小限制,如下:
-   *   图片(image): 1M,支持JPG格式
-   *   语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
-   *   视频(video):10MB,支持MP4格式
-   *   缩略图(thumb):64KB,支持JPG格式
-   *
-   * 详情请见: 新增临时素材
-   * 
- * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} - * @param fileType 文件类型,请看{@link me.chanjar.weixin.common.api.WxConsts} - * @param inputStream 输入流 - * @throws WxErrorException - */ - public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException; - - /** - *
-   * 上传非图文永久素材
-   *
-   * 上传的多媒体文件有格式和大小限制,如下:
-   *   图片(image): 图片大小不超过2M,支持bmp/png/jpeg/jpg/gif格式
-   *   语音(voice):语音大小不超过5M,长度不超过60秒,支持mp3/wma/wav/amr格式
-   *   视频(video):在上传视频素材时需要POST另一个表单,id为description,包含素材的描述信息,内容格式为JSON
-   *   缩略图(thumb):文档未说明
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki/14/7e6c03263063f4813141c3e17dd4350a.html
-   * 
- * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} - * @param material 上传的素材, 请看{@link me.chanjar.weixin.mp.bean.WxMpMaterial} - */ - public WxMpMaterialUploadResult materialFileUpload(String mediaType, WxMpMaterial material) throws WxErrorException; - - /** - *
-   * 上传永久图文素材
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki/14/7e6c03263063f4813141c3e17dd4350a.html
-   * 
- * @param news 上传的图文消息, 请看{@link me.chanjar.weixin.mp.bean.WxMpMaterialNews} - */ - public WxMpMaterialUploadResult materialNewsUpload(WxMpMaterialNews news) throws WxErrorException; - - /** - *
-   * 下载声音或者图片永久素材
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki/4/b3546879f07623cb30df9ca0e420a5d0.html
-   * 
- * @param media_id 永久素材的id - */ - public InputStream materialImageOrVoiceDownload(String media_id) throws WxErrorException; - - /** - *
-   * 获取视频永久素材的信息和下载地址
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki/4/b3546879f07623cb30df9ca0e420a5d0.html
-   * 
- * @param media_id 永久素材的id - */ - public WxMpMaterialVideoInfoResult materialVideoInfo(String media_id) throws WxErrorException; - - /** - *
-   * 获取图文永久素材的信息
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki/4/b3546879f07623cb30df9ca0e420a5d0.html
-   * 
- * @param media_id 永久素材的id - */ - public WxMpMaterialNews materialNewsInfo(String media_id) throws WxErrorException; - - /** - *
-   * 更新图文永久素材
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki/4/19a59cba020d506e767360ca1be29450.html
-   * 
- * @param wxMpMaterialArticleUpdate 用来更新图文素材的bean, 请看{@link me.chanjar.weixin.mp.bean.WxMpMaterialArticleUpdate} - */ - public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate) throws WxErrorException; - - /** - *
-   * 删除永久素材
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki/5/e66f61c303db51a6c0f90f46b15af5f5.html
-   * 
- * @param media_id 永久素材的id - */ - public boolean materialDelete(String media_id) throws WxErrorException; - - /** - *
-   * 获取各类素材总数
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki/16/8cc64f8c189674b421bee3ed403993b8.html
-   * 
- */ - public WxMpMaterialCountResult materialCount() throws WxErrorException; - - /** - *
-   * 分页获取图文素材列表
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki/12/2108cd7aafff7f388f41f37efa710204.html
-   * 
- * @param offset 从全部素材的该偏移位置开始返回,0表示从第一个素材 返回 - * @param count 返回素材的数量,取值在1到20之间 - */ - public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count) throws WxErrorException; - - /** - *
-   * 分页获取其他媒体素材列表
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki/12/2108cd7aafff7f388f41f37efa710204.html
-   * 
- * @param type 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} - * @param offset 从全部素材的该偏移位置开始返回,0表示从第一个素材 返回 - * @param count 返回素材的数量,取值在1到20之间 - */ - public WxMpMaterialFileBatchGetResult materialFileBatchGet(String type, int offset, int count) throws WxErrorException; - - /** - * 新增临时素材 - * @see #mediaUpload(String, String, InputStream) - * @param mediaType - * @param file - * @throws WxErrorException - */ - public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; - - /** - *
-   * 获取临时素材
-   * 本接口即为原“下载多媒体文件”接口。
-   * 根据微信文档,视频文件下载不了,会返回null
-   * 详情请见: 获取临时素材
-   * 
- * @param media_id - * @return 保存到本地的临时文件 - * @throws WxErrorException - */ - public File mediaDownload(String media_id) throws WxErrorException; - /** *
    * 发送客服消息
@@ -881,17 +730,6 @@ public interface WxMpService {
      */
   public WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws Exception;
 
-  /**
-     * 
-     * 上传图文消息内的图片获取URL
-     * 详情请见:http://mp.weixin.qq.com/wiki/15/40b6865b893947b764e2de8e4a1fb55f.html#.E4.B8.8A.E4.BC.A0.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF.E5.86.85.E7.9A.84.E5.9B.BE.E7.89.87.E8.8E.B7.E5.8F.96URL.E3.80.90.E8.AE.A2.E9.98.85.E5.8F.B7.E4.B8.8E.E6.9C.8D.E5.8A.A1.E5.8F.B7.E8.AE.A4.E8.AF.81.E5.90.8E.E5.9D.87.E5.8F.AF.E7.94.A8.E3.80.91
-     * 
- * @param file - * @return WxMediaImgUploadResult 返回图片url - * @throws WxErrorException - */ - public WxMediaImgUploadResult mediaImgUpload(File file) throws WxErrorException; - /** *
      * 设置所属行业
@@ -920,5 +758,16 @@ public interface WxMpService {
    * @return WxMpKefuService
    */ 
   WxMpKefuService getKefuService();
-  
+
+  /**
+   * 返回客服接口方法实现类,以方便调用个其各种接口
+   * @return WxMpMaterialService
+   */
+  WxMpMaterialService getMaterialService();
+
+  /**
+   * 获取WxMpConfigStorage 对象
+   * @return WxMpConfigStorage
+   */
+  WxMpConfigStorage getWxMpConfigStorage();
 }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
index b9fa37aa..8b7997a5 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
@@ -18,6 +18,7 @@ import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.UUID;
 
+import me.chanjar.weixin.mp.api.impl.WxMpMaterialServiceImpl;
 import org.apache.http.Consts;
 import org.apache.http.HttpHost;
 import org.apache.http.client.ClientProtocolException;
@@ -136,6 +137,8 @@ public class WxMpServiceImpl implements WxMpService {
   
   protected WxMpKefuService kefuService = new WxMpKefuServiceImpl(this);
 
+  protected WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this);
+
   protected CloseableHttpClient httpClient;
 
   protected HttpHost httpProxy;
@@ -309,119 +312,6 @@ public class WxMpServiceImpl implements WxMpService {
     }
   }
 
-  @Override
-  public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException {
-    return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType));
-  }
-
-  @Override
-  public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType;
-    return execute(new MediaUploadRequestExecutor(), url, file);
-  }
-
-  @Override
-  public File mediaDownload(String media_id) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/media/get";
-    return execute(new MediaDownloadRequestExecutor(this.wxMpConfigStorage.getTmpDirFile()), url, "media_id=" + media_id);
-  }
-
-  @Override
-  public WxMpMaterialUploadResult materialFileUpload(String mediaType, WxMpMaterial material) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/material/add_material?type=" + mediaType;
-    return execute(new MaterialUploadRequestExecutor(), url, material);
-  }
-
-  @Override
-  public WxMpMaterialUploadResult materialNewsUpload(WxMpMaterialNews news) throws WxErrorException {
-    if (news == null || news.isEmpty()) {
-      throw new IllegalArgumentException("news is empty!");
-    }
-    String url = "https://api.weixin.qq.com/cgi-bin/material/add_news";
-    String responseContent = post(url, news.toJson());
-    return WxMpMaterialUploadResult.fromJson(responseContent);
-  }
-
-  @Override
-  public InputStream materialImageOrVoiceDownload(String media_id) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/material/get_material";
-    return execute(new MaterialVoiceAndImageDownloadRequestExecutor(this.wxMpConfigStorage.getTmpDirFile()), url, media_id);
-  }
-
-  @Override
-  public WxMpMaterialVideoInfoResult materialVideoInfo(String media_id) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/material/get_material";
-    return execute(new MaterialVideoInfoRequestExecutor(), url, media_id);
-  }
-
-  @Override
-  public WxMpMaterialNews materialNewsInfo(String media_id) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/material/get_material";
-    return execute(new MaterialNewsInfoRequestExecutor(), url, media_id);
-  }
-
-  @Override
-  public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/material/update_news";
-    String responseText = post(url, wxMpMaterialArticleUpdate.toJson());
-    WxError wxError = WxError.fromJson(responseText);
-    if (wxError.getErrorCode() == 0) {
-      return true;
-    } else {
-      throw new WxErrorException(wxError);
-    }
-  }
-
-  @Override
-  public boolean materialDelete(String media_id) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/material/del_material";
-    return execute(new MaterialDeleteRequestExecutor(), url, media_id);
-  }
-
-  @Override
-  public WxMpMaterialCountResult materialCount() throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount";
-    String responseText = get(url, null);
-    WxError wxError = WxError.fromJson(responseText);
-    if (wxError.getErrorCode() == 0) {
-      return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialCountResult.class);
-    } else {
-      throw new WxErrorException(wxError);
-    }
-  }
-
-  @Override
-  public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material";
-    Map params = new HashMap<>();
-    params.put("type", WxConsts.MATERIAL_NEWS);
-    params.put("offset", offset);
-    params.put("count", count);
-    String responseText = post(url, WxGsonBuilder.create().toJson(params));
-    WxError wxError = WxError.fromJson(responseText);
-    if (wxError.getErrorCode() == 0) {
-      return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialNewsBatchGetResult.class);
-    } else {
-      throw new WxErrorException(wxError);
-    }
-  }
-
-  @Override
-  public WxMpMaterialFileBatchGetResult materialFileBatchGet(String type, int offset, int count) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material";
-    Map params = new HashMap<>();
-    params.put("type", type);
-    params.put("offset", offset);
-    params.put("count", count);
-    String responseText = post(url, WxGsonBuilder.create().toJson(params));
-    WxError wxError = WxError.fromJson(responseText);
-    if (wxError.getErrorCode() == 0) {
-      return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialFileBatchGetResult.class);
-    } else {
-      throw new WxErrorException(wxError);
-    }
-  }
-
   @Override
   public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException {
     String url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews";
@@ -877,6 +767,7 @@ public class WxMpServiceImpl implements WxMpService {
     this.httpClient = apacheHttpClientBuilder.build();
   }
 
+  @Override
   public WxMpConfigStorage getWxMpConfigStorage() {
     return this.wxMpConfigStorage;
   }
@@ -1406,12 +1297,6 @@ public class WxMpServiceImpl implements WxMpService {
     return WxMpMassSendResult.fromJson(responseContent);
   }
 
-  @Override
-  public WxMediaImgUploadResult mediaImgUpload(File file) throws WxErrorException {
-    String url = "https://api.weixin.qq.com/cgi-bin/media/uploadimg";
-    return execute(new MediaImgUploadRequestExecutor(), url, file);
-  }
-
   @Override
   public String setIndustry(WxMpIndustry wxMpIndustry) throws WxErrorException {
     if (null == wxMpIndustry.getPrimaryIndustry() || null == wxMpIndustry.getPrimaryIndustry().getId()
@@ -1433,5 +1318,10 @@ public class WxMpServiceImpl implements WxMpService {
   public WxMpKefuService getKefuService() {
     return this.kefuService;
   }
+
+  @Override
+  public WxMpMaterialService getMaterialService() {
+    return this.materialService;
+  }
   
 }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java
new file mode 100644
index 00000000..6befcc2b
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java
@@ -0,0 +1,155 @@
+package me.chanjar.weixin.mp.api.impl;
+
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.bean.result.WxError;
+import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.util.fs.FileUtils;
+import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor;
+import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
+import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
+import me.chanjar.weixin.common.util.json.WxGsonBuilder;
+import me.chanjar.weixin.mp.api.WxMpMaterialService;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.*;
+import me.chanjar.weixin.mp.bean.result.*;
+import me.chanjar.weixin.mp.util.http.*;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Created by Binary Wang on 2016/7/21.
+ */
+public class WxMpMaterialServiceImpl implements WxMpMaterialService {
+  private WxMpService wxMpService;
+
+  public WxMpMaterialServiceImpl(WxMpService wxMpService) {
+    this.wxMpService = wxMpService;
+  }
+
+  @Override
+  public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException {
+    return this.mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType));
+  }
+
+  @Override
+  public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType;
+    return this.wxMpService.execute(new MediaUploadRequestExecutor(), url, file);
+  }
+
+  @Override
+  public File mediaDownload(String media_id) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/media/get";
+    return this.wxMpService.execute(new MediaDownloadRequestExecutor(this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), url, "media_id=" + media_id);
+  }
+
+  @Override
+  public WxMpMaterialUploadResult materialFileUpload(String mediaType, WxMpMaterial material) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/material/add_material?type=" + mediaType;
+    return this.wxMpService.execute(new MaterialUploadRequestExecutor(), url, material);
+  }
+
+  @Override
+  public WxMpMaterialUploadResult materialNewsUpload(WxMpMaterialNews news) throws WxErrorException {
+    if (news == null || news.isEmpty()) {
+      throw new IllegalArgumentException("news is empty!");
+    }
+    String url = "https://api.weixin.qq.com/cgi-bin/material/add_news";
+    String responseContent = this.wxMpService.post(url, news.toJson());
+    return WxMpMaterialUploadResult.fromJson(responseContent);
+  }
+
+  @Override
+  public InputStream materialImageOrVoiceDownload(String media_id) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/material/get_material";
+    return this.wxMpService.execute(new MaterialVoiceAndImageDownloadRequestExecutor(this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), url, media_id);
+  }
+
+  @Override
+  public WxMpMaterialVideoInfoResult materialVideoInfo(String media_id) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/material/get_material";
+    return this.wxMpService.execute(new MaterialVideoInfoRequestExecutor(), url, media_id);
+  }
+
+  @Override
+  public WxMpMaterialNews materialNewsInfo(String media_id) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/material/get_material";
+    return this.wxMpService.execute(new MaterialNewsInfoRequestExecutor(), url, media_id);
+  }
+
+  @Override
+  public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/material/update_news";
+    String responseText = this.wxMpService.post(url, wxMpMaterialArticleUpdate.toJson());
+    WxError wxError = WxError.fromJson(responseText);
+    if (wxError.getErrorCode() == 0) {
+      return true;
+    } else {
+      throw new WxErrorException(wxError);
+    }
+  }
+
+  @Override
+  public boolean materialDelete(String media_id) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/material/del_material";
+    return this.wxMpService.execute(new MaterialDeleteRequestExecutor(), url, media_id);
+  }
+
+  @Override
+  public WxMpMaterialCountResult materialCount() throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount";
+    String responseText = this.wxMpService.get(url, null);
+    WxError wxError = WxError.fromJson(responseText);
+    if (wxError.getErrorCode() == 0) {
+      return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialCountResult.class);
+    } else {
+      throw new WxErrorException(wxError);
+    }
+  }
+
+  @Override
+  public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material";
+    Map params = new HashMap<>();
+    params.put("type", WxConsts.MATERIAL_NEWS);
+    params.put("offset", offset);
+    params.put("count", count);
+    String responseText = this.wxMpService.post(url, WxGsonBuilder.create().toJson(params));
+    WxError wxError = WxError.fromJson(responseText);
+    if (wxError.getErrorCode() == 0) {
+      return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialNewsBatchGetResult.class);
+    } else {
+      throw new WxErrorException(wxError);
+    }
+  }
+
+  @Override
+  public WxMpMaterialFileBatchGetResult materialFileBatchGet(String type, int offset, int count) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material";
+    Map params = new HashMap<>();
+    params.put("type", type);
+    params.put("offset", offset);
+    params.put("count", count);
+    String responseText = this.wxMpService.post(url, WxGsonBuilder.create().toJson(params));
+    WxError wxError = WxError.fromJson(responseText);
+    if (wxError.getErrorCode() == 0) {
+      return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialFileBatchGetResult.class);
+    } else {
+      throw new WxErrorException(wxError);
+    }
+  }
+
+  @Override
+  public WxMediaImgUploadResult mediaImgUpload(File file) throws WxErrorException {
+    String url = "https://api.weixin.qq.com/cgi-bin/media/uploadimg";
+    return this.wxMpService.execute(new MediaImgUploadRequestExecutor(), url, file);
+  }
+
+}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java
index 73ebd1e2..6d276efd 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java
@@ -91,7 +91,7 @@ public class WxMpMassMessageAPITest {
     {
       // 上传视频到媒体库
       InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.mp4");
-      WxMediaUploadResult uploadMediaRes = wxService.mediaUpload(WxConsts.MEDIA_VIDEO, WxConsts.FILE_MP4, inputStream);
+      WxMediaUploadResult uploadMediaRes = wxService.getMaterialService().mediaUpload(WxConsts.MEDIA_VIDEO, WxConsts.FILE_MP4, inputStream);
       Assert.assertNotNull(uploadMediaRes);
       Assert.assertNotNull(uploadMediaRes.getMediaId());
       
@@ -110,7 +110,7 @@ public class WxMpMassMessageAPITest {
      */
     {
       InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg");
-      WxMediaUploadResult uploadMediaRes = wxService.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, inputStream);
+      WxMediaUploadResult uploadMediaRes = wxService.getMaterialService().mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, inputStream);
       Assert.assertNotNull(uploadMediaRes);
       Assert.assertNotNull(uploadMediaRes.getMediaId());
       messages[1] = new Object[] { WxConsts.MASS_MSG_IMAGE, uploadMediaRes.getMediaId() };
@@ -120,7 +120,7 @@ public class WxMpMassMessageAPITest {
      */
     {
       InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.mp3");
-      WxMediaUploadResult uploadMediaRes = wxService.mediaUpload(WxConsts.MEDIA_VOICE, WxConsts.FILE_MP3, inputStream);
+      WxMediaUploadResult uploadMediaRes = wxService.getMaterialService().mediaUpload(WxConsts.MEDIA_VOICE, WxConsts.FILE_MP3, inputStream);
       Assert.assertNotNull(uploadMediaRes);
       Assert.assertNotNull(uploadMediaRes.getMediaId());
       messages[2] = new Object[] { WxConsts.MASS_MSG_VOICE, uploadMediaRes.getMediaId() };
@@ -131,7 +131,7 @@ public class WxMpMassMessageAPITest {
     {
       // 上传照片到媒体库
       InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg");
-      WxMediaUploadResult uploadMediaRes = wxService.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, inputStream);
+      WxMediaUploadResult uploadMediaRes = wxService.getMaterialService().mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, inputStream);
       Assert.assertNotNull(uploadMediaRes);
       Assert.assertNotNull(uploadMediaRes.getMediaId());
       
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMediaAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMediaAPITest.java
deleted file mode 100644
index b2fbc851..00000000
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMediaAPITest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package me.chanjar.weixin.mp.api;
-
-import com.google.inject.Inject;
-import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
-import me.chanjar.weixin.common.exception.WxErrorException;
-import org.testng.Assert;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Guice;
-import org.testng.annotations.Test;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 测试多媒体文件上传下载
- * @author chanjarster
- *
- */
-@Test(groups="mediaAPI", dependsOnGroups="baseAPI")
-@Guice(modules = ApiTestModule.class)
-public class WxMpMediaAPITest {
-
-  @Inject
-  protected WxMpServiceImpl wxService;
-
-  private List media_ids = new ArrayList<>();
-  
-  @Test(dataProvider="uploadMedia")
-  public void testUploadMedia(String mediaType, String fileType, String fileName) throws WxErrorException, IOException {
-    try(InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName)){
-      WxMediaUploadResult res = this.wxService.mediaUpload(mediaType, fileType, inputStream);
-      Assert.assertNotNull(res.getType());
-      Assert.assertNotNull(res.getCreatedAt());
-      Assert.assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null);
-      
-      if (res.getMediaId() != null) {
-        this.media_ids.add(res.getMediaId());
-      }
-      
-      if (res.getThumbMediaId() != null) {
-        this.media_ids.add(res.getThumbMediaId());
-      }
-    }
-  }
-  
-  @DataProvider
-  public Object[][] uploadMedia() {
-    return new Object[][] {
-        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_JPG, "mm.jpeg" }
-    };
-  }
-  
-  @Test(dependsOnMethods = { "testUploadMedia" }, dataProvider="downloadMedia")
-  public void testDownloadMedia(String media_id) throws WxErrorException {
-    this.wxService.mediaDownload(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;
-  }
-  
-}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMaterialAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java
similarity index 63%
rename from weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMaterialAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java
index 8e145e15..abe96221 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMaterialAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java
@@ -1,9 +1,12 @@
-package me.chanjar.weixin.mp.api;
+package me.chanjar.weixin.mp.api.impl;
 
 import com.google.inject.Inject;
 import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
 import me.chanjar.weixin.common.exception.WxErrorException;
 import me.chanjar.weixin.common.util.fs.FileUtils;
+import me.chanjar.weixin.mp.api.ApiTestModule;
+import me.chanjar.weixin.mp.api.WxMpServiceImpl;
 import me.chanjar.weixin.mp.bean.WxMpMaterial;
 import me.chanjar.weixin.mp.bean.WxMpMaterialArticleUpdate;
 import me.chanjar.weixin.mp.bean.WxMpMaterialNews;
@@ -20,14 +23,15 @@ import java.io.InputStream;
 import java.util.*;
 
 /**
- * 测试多媒体文件上传下载
+ * 素材管理相关接口的测试
  *
+ * @author chanjarster
  * @author codepiano
+ * @author Binary Wang
  */
 @Test(groups = "materialAPI")
 @Guice(modules = ApiTestModule.class)
-public class WxMpMaterialAPITest {
-
+public class WxMpMaterialServiceImplTest {
   @Inject
   protected WxMpServiceImpl wxService;
 
@@ -41,10 +45,20 @@ public class WxMpMaterialAPITest {
   // 先查询保存测试开始前永久素材数据
   private WxMpMaterialCountResult wxMaterialCountResultBeforeTest;
 
-  @Test(dataProvider = "uploadMaterial")
+  @DataProvider
+  public Object[][] uploadMedia() {
+    return new Object[][] {
+            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_JPG, "mm.jpeg" }
+    };
+  }
+
+  @Test(dataProvider = "uploadMedia")
   public void testUploadMaterial(String mediaType, String fileType, String fileName) throws WxErrorException, IOException {
     if (wxMaterialCountResultBeforeTest == null) {
-      wxMaterialCountResultBeforeTest = wxService.materialCount();
+      wxMaterialCountResultBeforeTest = this.wxService.getMaterialService().materialCount();
     }
     InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName);
     File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType);
@@ -55,7 +69,7 @@ public class WxMpMaterialAPITest {
       wxMaterial.setVideoTitle("title");
       wxMaterial.setVideoIntroduction("test video description");
     }
-    WxMpMaterialUploadResult res = wxService.materialFileUpload(mediaType, wxMaterial);
+    WxMpMaterialUploadResult res = this.wxService.getMaterialService().materialFileUpload(mediaType, wxMaterial);
     Assert.assertNotNull(res.getMediaId());
     if (WxConsts.MEDIA_IMAGE.equals(mediaType) || WxConsts.MEDIA_THUMB.equals(mediaType)) {
       Assert.assertNotNull(res.getUrl());
@@ -69,6 +83,8 @@ public class WxMpMaterialAPITest {
     materialInfo.put("length", tempFile.length());
     materialInfo.put("filename", tempFile.getName());
     media_ids.put(res.getMediaId(), materialInfo);
+
+    System.out.println(res);
   }
 
   @Test(dependsOnMethods = {"testUploadMaterial"})
@@ -88,14 +104,14 @@ public class WxMpMaterialAPITest {
 
     // 多图文消息
     WxMpMaterialNews wxMpMaterialNewsMultiple = new WxMpMaterialNews();
-    WxMpMaterialNews.WxMpMaterialNewsArticle wxMpMaterialNewsArticleMutiple1 = new WxMpMaterialNews.WxMpMaterialNewsArticle();
-    wxMpMaterialNewsArticleMutiple1.setAuthor("author1");
-    wxMpMaterialNewsArticleMutiple1.setThumbMediaId(thumbMediaId);
-    wxMpMaterialNewsArticleMutiple1.setTitle("multi title1");
-    wxMpMaterialNewsArticleMutiple1.setContent("content 1");
-    wxMpMaterialNewsArticleMutiple1.setContentSourceUrl("content url");
-    wxMpMaterialNewsArticleMutiple1.setShowCoverPic(true);
-    wxMpMaterialNewsArticleMutiple1.setDigest("");
+    WxMpMaterialNews.WxMpMaterialNewsArticle wxMpMaterialNewsArticleMultiple1 = new WxMpMaterialNews.WxMpMaterialNewsArticle();
+    wxMpMaterialNewsArticleMultiple1.setAuthor("author1");
+    wxMpMaterialNewsArticleMultiple1.setThumbMediaId(thumbMediaId);
+    wxMpMaterialNewsArticleMultiple1.setTitle("multi title1");
+    wxMpMaterialNewsArticleMultiple1.setContent("content 1");
+    wxMpMaterialNewsArticleMultiple1.setContentSourceUrl("content url");
+    wxMpMaterialNewsArticleMultiple1.setShowCoverPic(true);
+    wxMpMaterialNewsArticleMultiple1.setDigest("");
 
     WxMpMaterialNews.WxMpMaterialNewsArticle wxMpMaterialNewsArticleMultiple2 = new WxMpMaterialNews.WxMpMaterialNewsArticle();
     wxMpMaterialNewsArticleMultiple2.setAuthor("author2");
@@ -106,18 +122,18 @@ public class WxMpMaterialAPITest {
     wxMpMaterialNewsArticleMultiple2.setShowCoverPic(true);
     wxMpMaterialNewsArticleMultiple2.setDigest("");
 
-    wxMpMaterialNewsMultiple.addArticle(wxMpMaterialNewsArticleMutiple1);
+    wxMpMaterialNewsMultiple.addArticle(wxMpMaterialNewsArticleMultiple1);
     wxMpMaterialNewsMultiple.addArticle(wxMpMaterialNewsArticleMultiple2);
 
-    WxMpMaterialUploadResult resSingle = wxService.materialNewsUpload(wxMpMaterialNewsSingle);
+    WxMpMaterialUploadResult resSingle = this.wxService.getMaterialService().materialNewsUpload(wxMpMaterialNewsSingle);
     singleNewsMediaId = resSingle.getMediaId();
-    WxMpMaterialUploadResult resMulti = wxService.materialNewsUpload(wxMpMaterialNewsMultiple);
+    WxMpMaterialUploadResult resMulti = this.wxService.getMaterialService().materialNewsUpload(wxMpMaterialNewsMultiple);
     multiNewsMediaId = resMulti.getMediaId();
   }
 
   @Test(dependsOnMethods = {"testAddNews"})
   public void testMaterialCount() throws WxErrorException {
-    WxMpMaterialCountResult wxMaterialCountResult = wxService.materialCount();
+    WxMpMaterialCountResult wxMaterialCountResult = this.wxService.getMaterialService().materialCount();
     // 测试上传过程中添加了一个音频,一个视频,两个图片,两个图文消息
     Assert.assertEquals(wxMaterialCountResultBeforeTest.getVoiceCount() + 1, wxMaterialCountResult.getVoiceCount());
     Assert.assertEquals(wxMaterialCountResultBeforeTest.getVideoCount() + 1, wxMaterialCountResult.getVideoCount());
@@ -125,29 +141,18 @@ public class WxMpMaterialAPITest {
     Assert.assertEquals(wxMaterialCountResultBeforeTest.getNewsCount() + 2, wxMaterialCountResult.getNewsCount());
   }
 
-
-  @DataProvider
-  public Object[][] uploadMaterial() {
-    return new Object[][]{
-        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_JPG, "mm.jpeg"}
-    };
-  }
-
   @Test(dependsOnMethods = {"testMaterialCount"}, dataProvider = "downloadMaterial")
   public void testDownloadMaterial(String media_id) throws WxErrorException, IOException {
     Map materialInfo = media_ids.get(media_id);
     Assert.assertNotNull(materialInfo);
     String filename = materialInfo.get("filename").toString();
     if (filename.endsWith(".mp3") || filename.endsWith(".jpeg")) {
-      InputStream inputStream = wxService.materialImageOrVoiceDownload(media_id);
+      InputStream inputStream = this.wxService.getMaterialService().materialImageOrVoiceDownload(media_id);
       Assert.assertNotNull(inputStream);
       IOUtils.closeQuietly(inputStream);
     }
     if (filename.endsWith("mp4")) {
-      WxMpMaterialVideoInfoResult wxMaterialVideoInfoResult = wxService.materialVideoInfo(media_id);
+      WxMpMaterialVideoInfoResult wxMaterialVideoInfoResult = this.wxService.getMaterialService().materialVideoInfo(media_id);
       Assert.assertNotNull(wxMaterialVideoInfoResult);
       Assert.assertNotNull(wxMaterialVideoInfoResult.getDownUrl());
     }
@@ -155,15 +160,15 @@ public class WxMpMaterialAPITest {
 
   @Test(dependsOnMethods = {"testAddNews"})
   public void testGetNewsInfo() throws WxErrorException {
-    WxMpMaterialNews wxMpMaterialNewsSingle = wxService.materialNewsInfo(singleNewsMediaId);
-    WxMpMaterialNews wxMpMaterialNewsMultiple = wxService.materialNewsInfo(multiNewsMediaId);
+    WxMpMaterialNews wxMpMaterialNewsSingle = this.wxService.getMaterialService().materialNewsInfo(singleNewsMediaId);
+    WxMpMaterialNews wxMpMaterialNewsMultiple = this.wxService.getMaterialService().materialNewsInfo(multiNewsMediaId);
     Assert.assertNotNull(wxMpMaterialNewsSingle);
     Assert.assertNotNull(wxMpMaterialNewsMultiple);
   }
 
   @Test(dependsOnMethods = {"testGetNewsInfo"})
   public void testUpdateNewsInfo() throws WxErrorException {
-    WxMpMaterialNews wxMpMaterialNewsSingle = wxService.materialNewsInfo(singleNewsMediaId);
+    WxMpMaterialNews wxMpMaterialNewsSingle = this.wxService.getMaterialService().materialNewsInfo(singleNewsMediaId);
     Assert.assertNotNull(wxMpMaterialNewsSingle);
     WxMpMaterialArticleUpdate wxMpMaterialArticleUpdateSingle = new WxMpMaterialArticleUpdate();
     WxMpMaterialNews.WxMpMaterialNewsArticle articleSingle = wxMpMaterialNewsSingle.getArticles().get(0);
@@ -171,13 +176,13 @@ public class WxMpMaterialAPITest {
     wxMpMaterialArticleUpdateSingle.setMediaId(singleNewsMediaId);
     wxMpMaterialArticleUpdateSingle.setArticles(articleSingle);
     wxMpMaterialArticleUpdateSingle.setIndex(0);
-    boolean resultSingle = wxService.materialNewsUpdate(wxMpMaterialArticleUpdateSingle);
+    boolean resultSingle = this.wxService.getMaterialService().materialNewsUpdate(wxMpMaterialArticleUpdateSingle);
     Assert.assertTrue(resultSingle);
-    wxMpMaterialNewsSingle = wxService.materialNewsInfo(singleNewsMediaId);
+    wxMpMaterialNewsSingle = this.wxService.getMaterialService().materialNewsInfo(singleNewsMediaId);
     Assert.assertNotNull(wxMpMaterialNewsSingle);
     Assert.assertEquals("content single update", wxMpMaterialNewsSingle.getArticles().get(0).getContent());
 
-    WxMpMaterialNews wxMpMaterialNewsMultiple = wxService.materialNewsInfo(multiNewsMediaId);
+    WxMpMaterialNews wxMpMaterialNewsMultiple = this.wxService.getMaterialService().materialNewsInfo(multiNewsMediaId);
     Assert.assertNotNull(wxMpMaterialNewsMultiple);
     WxMpMaterialArticleUpdate wxMpMaterialArticleUpdateMulti = new WxMpMaterialArticleUpdate();
     WxMpMaterialNews.WxMpMaterialNewsArticle articleMulti = wxMpMaterialNewsMultiple.getArticles().get(1);
@@ -185,31 +190,30 @@ public class WxMpMaterialAPITest {
     wxMpMaterialArticleUpdateMulti.setMediaId(multiNewsMediaId);
     wxMpMaterialArticleUpdateMulti.setArticles(articleMulti);
     wxMpMaterialArticleUpdateMulti.setIndex(1);
-    boolean resultMulti = wxService.materialNewsUpdate(wxMpMaterialArticleUpdateMulti);
+    boolean resultMulti = this.wxService.getMaterialService().materialNewsUpdate(wxMpMaterialArticleUpdateMulti);
     Assert.assertTrue(resultMulti);
-    wxMpMaterialNewsMultiple = wxService.materialNewsInfo(multiNewsMediaId);
+    wxMpMaterialNewsMultiple = this.wxService.getMaterialService().materialNewsInfo(multiNewsMediaId);
     Assert.assertNotNull(wxMpMaterialNewsMultiple);
     Assert.assertEquals("content 2 update", wxMpMaterialNewsMultiple.getArticles().get(1).getContent());
   }
 
-
   @Test(dependsOnMethods = {"testUpdateNewsInfo"})
   public void testMaterialNewsList() throws WxErrorException {
-    WxMpMaterialNewsBatchGetResult wxMpMaterialNewsBatchGetResult = wxService.materialNewsBatchGet(0, 20);
+    WxMpMaterialNewsBatchGetResult wxMpMaterialNewsBatchGetResult = this.wxService.getMaterialService().materialNewsBatchGet(0, 20);
     return;
   }
 
   @Test(dependsOnMethods = {"testMaterialNewsList"})
   public void testMaterialFileList() throws WxErrorException {
-    WxMpMaterialFileBatchGetResult wxMpMaterialVoiceBatchGetResult = wxService.materialFileBatchGet(WxConsts.MATERIAL_VOICE, 0, 20);
-    WxMpMaterialFileBatchGetResult wxMpMaterialVideoBatchGetResult = wxService.materialFileBatchGet(WxConsts.MATERIAL_VIDEO, 0, 20);
-    WxMpMaterialFileBatchGetResult wxMpMaterialImageBatchGetResult = wxService.materialFileBatchGet(WxConsts.MATERIAL_IMAGE, 0, 20);
+    WxMpMaterialFileBatchGetResult wxMpMaterialVoiceBatchGetResult = this.wxService.getMaterialService().materialFileBatchGet(WxConsts.MATERIAL_VOICE, 0, 20);
+    WxMpMaterialFileBatchGetResult wxMpMaterialVideoBatchGetResult = this.wxService.getMaterialService().materialFileBatchGet(WxConsts.MATERIAL_VIDEO, 0, 20);
+    WxMpMaterialFileBatchGetResult wxMpMaterialImageBatchGetResult = this.wxService.getMaterialService().materialFileBatchGet(WxConsts.MATERIAL_IMAGE, 0, 20);
     return;
   }
 
   @Test(dependsOnMethods = {"testMaterialFileList"}, dataProvider = "allTestMaterial")
   public void testDeleteMaterial(String mediaId) throws WxErrorException {
-    boolean result = wxService.materialDelete(mediaId);
+    boolean result = this.wxService.getMaterialService().materialDelete(mediaId);
     Assert.assertTrue(result);
   }
 
@@ -234,4 +238,42 @@ public class WxMpMaterialAPITest {
     params.add(new Object[]{this.multiNewsMediaId});
     return params.iterator();
   }
+
+  // 以下为media接口的测试
+  private List mediaIds = new ArrayList<>();
+  @Test(dataProvider="uploadMedia")
+  public void testUploadMedia(String mediaType, String fileType, String fileName) throws WxErrorException, IOException {
+    try(InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName)){
+      WxMediaUploadResult res = this.wxService.getMaterialService().mediaUpload(mediaType, fileType, inputStream);
+      Assert.assertNotNull(res.getType());
+      Assert.assertNotNull(res.getCreatedAt());
+      Assert.assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null);
+
+      if (res.getMediaId() != null) {
+        this.mediaIds.add(res.getMediaId());
+      }
+
+      if (res.getThumbMediaId() != null) {
+        this.mediaIds.add(res.getThumbMediaId());
+      }
+
+      System.out.println(res);
+    }
+  }
+
+  @Test(dependsOnMethods = { "testUploadMedia" }, dataProvider="downloadMedia")
+  public void testDownloadMedia(String media_id) throws WxErrorException {
+    File file = this.wxService.getMaterialService().mediaDownload(media_id);
+    Assert.assertNotNull(file);
+    System.out.println(file.getAbsolutePath());
+  }
+
+  @DataProvider
+  public Object[][] downloadMedia() {
+    Object[][] params = new Object[this.mediaIds.size()][];
+    for (int i = 0; i < this.mediaIds.size(); i++) {
+      params[i] = new Object[] { this.mediaIds.get(i) };
+    }
+    return params;
+  }
 }
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java
index 8cc50542..a22f28d7 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java
@@ -18,7 +18,7 @@ public class DemoImageHandler implements WxMpMessageHandler {
   public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context,
       WxMpService wxMpService, WxSessionManager sessionManager) {
     try {
-      WxMediaUploadResult wxMediaUploadResult = wxMpService
+      WxMediaUploadResult wxMediaUploadResult = wxMpService.getMaterialService()
           .mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, ClassLoader.getSystemResourceAsStream("mm.jpeg"));
       WxMpXmlOutImageMessage m
           = WxMpXmlOutMessage
diff --git a/weixin-java-mp/src/test/resources/testng.xml b/weixin-java-mp/src/test/resources/testng.xml
index cb1263d3..003af428 100644
--- a/weixin-java-mp/src/test/resources/testng.xml
+++ b/weixin-java-mp/src/test/resources/testng.xml
@@ -3,19 +3,18 @@
 
 	
 		
-      
+      		
 			
 			
 			
 			
 			
-			
 			
 			
 			
 			
-      
-      
+			
+