mirror of
				https://gitee.com/binary/weixin-java-tools.git
				synced 2025-10-31 18:46:10 +08:00 
			
		
		
		
	添加上传媒体文件支持
This commit is contained in:
		
							
								
								
									
										8
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								pom.xml
									
									
									
									
									
								
							| @ -12,6 +12,7 @@ | ||||
| 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||
| 		<downloadJavadocs>true</downloadJavadocs> | ||||
| 		<downloadSources>true</downloadSources> | ||||
| 		<httpclient.version>4.3.5</httpclient.version> | ||||
| 	</properties> | ||||
| 	<dependencies> | ||||
| 		<dependency> | ||||
| @ -23,7 +24,12 @@ | ||||
| 		<dependency> | ||||
| 			<groupId>org.apache.httpcomponents</groupId> | ||||
| 			<artifactId>fluent-hc</artifactId> | ||||
| 			<version>4.3.5</version> | ||||
| 			<version>${httpclient.version}</version> | ||||
| 		</dependency> | ||||
|     <dependency> | ||||
|       <groupId>org.apache.httpcomponents</groupId> | ||||
|       <artifactId>httpmime</artifactId> | ||||
|       <version>${httpclient.version}</version> | ||||
|     </dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>javax.xml.bind</groupId> | ||||
|  | ||||
| @ -20,6 +20,17 @@ public class WxConsts { | ||||
|   public static final String EVT_VIEW = "VIEW"; | ||||
|   public static final String EVT_MASS_SEND_JOB_FINISH = "MASSSENDJOBFINISH"; | ||||
|  | ||||
|   public static final String MEDIA_IMAGE = "image"; | ||||
|   public static final String MEDIA_VOICE = "voice"; | ||||
|   public static final String MEDIA_VIDEO = "video"; | ||||
|   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"; | ||||
|  | ||||
|   public static final String ST_SEND_SUCCESS = "send success"; | ||||
|   public static final String ST_SEND_FAIL = "send fail"; | ||||
|   public static final String ST_涉嫌广告 = "err(10001)";  | ||||
|  | ||||
| @ -1,7 +1,12 @@ | ||||
| package chanjarster.weixin.api; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
|  | ||||
| import chanjarster.weixin.bean.WxCustomMessage; | ||||
| import chanjarster.weixin.bean.WxMenu; | ||||
| import chanjarster.weixin.bean.result.WxUploadResult; | ||||
| import chanjarster.weixin.exception.WxErrorException; | ||||
|  | ||||
| /** | ||||
| @ -24,7 +29,7 @@ public interface WxService { | ||||
|   /** | ||||
|    * <pre> | ||||
|    * 获取access_token,本方法线程安全 | ||||
|    * 且在多线程同时刷新时只刷新一次,避免超出200次/日的调用次数上限 | ||||
|    * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限 | ||||
|    *  | ||||
|    * 另:本service的所有方法都会在access_token过期是调用此方法 | ||||
|    *  | ||||
| @ -36,6 +41,32 @@ public interface WxService { | ||||
|    */ | ||||
|   public void refreshAccessToken() throws WxErrorException; | ||||
|    | ||||
|   /** | ||||
|    * <pre> | ||||
|    * 上传多媒体文件 | ||||
|    * 上传的多媒体文件有格式和大小限制,如下: | ||||
|    *   图片(image): 1M,支持JPG格式 | ||||
|    *   语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式 | ||||
|    *   视频(video):10MB,支持MP4格式 | ||||
|    *   缩略图(thumb):64KB,支持JPG格式 | ||||
|    *     | ||||
|    * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件 | ||||
|    * </pre> | ||||
|    * @param mediaType         媒体类型, 请看{@link WxConsts} | ||||
|    * @param fileType          文件类型,请看{@link WxConsts} | ||||
|    * @param inputStream       输入流 | ||||
|    * @throws WxErrorException | ||||
|    */ | ||||
|   public WxUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException; | ||||
|  | ||||
|   /** | ||||
|    * @see #uploadMedia(String, String, InputStream) | ||||
|    * @param mediaType | ||||
|    * @param file | ||||
|    * @throws WxErrorException | ||||
|    */ | ||||
|   public WxUploadResult uploadMedia(String mediaType, File file) throws WxErrorException; | ||||
|    | ||||
|   /** | ||||
|    * <pre> | ||||
|    * 发送客服消息 | ||||
|  | ||||
| @ -1,26 +1,33 @@ | ||||
| package chanjarster.weixin.api; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.security.MessageDigest; | ||||
| import java.util.Arrays; | ||||
| 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; | ||||
|  | ||||
| import chanjarster.weixin.bean.WxAccessToken; | ||||
| import chanjarster.weixin.bean.WxCustomMessage; | ||||
| import chanjarster.weixin.bean.WxError; | ||||
| 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; | ||||
|  | ||||
| @ -132,11 +139,21 @@ public class WxServiceImpl implements WxService { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected String post(String uri, String data) throws WxErrorException { | ||||
|   public WxUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException { | ||||
|     return uploadMedia(mediaType, createTmpFile(inputStream, 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); | ||||
|   } | ||||
|    | ||||
|   protected String post(String uri, Object data) throws WxErrorException { | ||||
|     return execute("POST", uri, data); | ||||
|   } | ||||
|    | ||||
|   protected String get(String uri, String data) throws WxErrorException { | ||||
|   protected String get(String uri, Object data) throws WxErrorException { | ||||
|     return execute("GET", uri, data); | ||||
|   } | ||||
|  | ||||
| @ -146,7 +163,7 @@ public class WxServiceImpl implements WxService { | ||||
|    * @return 微信服务端返回的结果 | ||||
|    * @throws WxErrorException  | ||||
|    */ | ||||
|   protected String execute(String method, String uri, String data) throws WxErrorException { | ||||
|   protected String execute(String method, String uri, Object data) throws WxErrorException { | ||||
|     if (StringUtils.isBlank(wxConfigStorage.getAccessToken())) { | ||||
|       refreshAccessToken(); | ||||
|     } | ||||
| @ -160,18 +177,30 @@ public class WxServiceImpl implements WxService { | ||||
|       if ("POST".equals(method)) { | ||||
|         HttpPost httpPost = new HttpPost(uriWithAccessToken); | ||||
|         if (data != null) { | ||||
|           StringEntity entity = new StringEntity(data, Consts.UTF_8); | ||||
|           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) { | ||||
|           uriWithAccessToken += uriWithAccessToken.endsWith("&") ? data : '&' + data; | ||||
|           if (data instanceof String) { | ||||
|             uriWithAccessToken += uriWithAccessToken.endsWith("&") ? data : '&' + (String)data; | ||||
|           } | ||||
|         } | ||||
|         HttpGet httpGet = new HttpGet(uriWithAccessToken); | ||||
|         CloseableHttpResponse response = httpclient.execute(httpGet); | ||||
|         response.setHeader("Content-Type", ContentType.APPLICATION_JSON.toString()); | ||||
|         resultContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | ||||
|       } | ||||
|        | ||||
| @ -196,8 +225,38 @@ 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; | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| package chanjarster.weixin.bean; | ||||
| package chanjarster.weixin.bean.result; | ||||
| 
 | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| @ -0,0 +1,54 @@ | ||||
| package chanjarster.weixin.bean.result; | ||||
|  | ||||
| import chanjarster.weixin.util.WxGsonBuilder; | ||||
|  | ||||
| public class WxUploadResult { | ||||
|  | ||||
|   protected String type; | ||||
|   protected String media_id; | ||||
|   protected String thumb_media_id; | ||||
|   protected long created_at; | ||||
|  | ||||
|   public String getType() { | ||||
|     return type; | ||||
|   } | ||||
|  | ||||
|   public void setType(String type) { | ||||
|     this.type = type; | ||||
|   } | ||||
|  | ||||
|   public String getMedia_id() { | ||||
|     return media_id; | ||||
|   } | ||||
|  | ||||
|   public void setMedia_id(String media_id) { | ||||
|     this.media_id = media_id; | ||||
|   } | ||||
|  | ||||
|   public long getCreated_at() { | ||||
|     return created_at; | ||||
|   } | ||||
|  | ||||
|   public void setCreated_at(long created_at) { | ||||
|     this.created_at = created_at; | ||||
|   } | ||||
|  | ||||
|   public static WxUploadResult fromJson(String json) { | ||||
|     return WxGsonBuilder.create().fromJson(json, WxUploadResult.class); | ||||
|   } | ||||
|  | ||||
|   public String getThumb_media_id() { | ||||
|     return thumb_media_id; | ||||
|   } | ||||
|  | ||||
|   public void setThumb_media_id(String thumb_media_id) { | ||||
|     this.thumb_media_id = thumb_media_id; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String toString() { | ||||
|     return "WxUploadResult [type=" + type + ", media_id=" + media_id + ", thumb_media_id=" + thumb_media_id | ||||
|         + ", created_at=" + created_at + "]"; | ||||
|   } | ||||
|  | ||||
| } | ||||
| @ -1,6 +1,6 @@ | ||||
| package chanjarster.weixin.exception; | ||||
|  | ||||
| import chanjarster.weixin.bean.WxError; | ||||
| import chanjarster.weixin.bean.result.WxError; | ||||
|  | ||||
| public class WxErrorException extends Exception { | ||||
|  | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| package chanjarster.weixin.api; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
|  | ||||
| import javax.xml.bind.JAXBException; | ||||
| @ -16,6 +17,7 @@ import org.testng.annotations.Test; | ||||
| import chanjarster.weixin.bean.WxCustomMessage; | ||||
| 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; | ||||
|  | ||||
| @ -26,12 +28,12 @@ public class WxServiceTest { | ||||
|   @BeforeTest | ||||
|   public void prepare() throws JAXBException { | ||||
|     InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | ||||
|     WxXmlConfigStorage config1 = XmlTransformer.fromXml(WxXmlConfigStorage.class, is1); | ||||
|     WxXmlConfigStorage config = XmlTransformer.fromXml(WxXmlConfigStorage.class, is1); | ||||
|     this.wxService = new WxServiceImpl(); | ||||
|     this.wxService.setWxConfigStorage(config1); | ||||
|     this.wxService.setWxConfigStorage(config); | ||||
|   } | ||||
|    | ||||
|   @Test | ||||
|   @Test() | ||||
|   public void testRefreshAccessToken() throws WxErrorException { | ||||
|     WxConfigStorage configStorage = wxService.wxConfigStorage; | ||||
|     String before = configStorage.getAccessToken(); | ||||
| @ -43,7 +45,7 @@ public class WxServiceTest { | ||||
|     Assert.assertTrue(StringUtils.isNotBlank(after)); | ||||
|   } | ||||
|    | ||||
|   @Test(dependsOnMethods = "testRefreshAccessToken") | ||||
|   @Test(dependsOnMethods = "testRefreshAccessToken", enabled = false) | ||||
|   public void sendCustomMessage() throws WxErrorException { | ||||
|     WxXmlConfigStorage configProvider = (WxXmlConfigStorage) wxService.wxConfigStorage; | ||||
|     WxCustomMessage message = new WxCustomMessage(); | ||||
| @ -54,22 +56,41 @@ public class WxServiceTest { | ||||
|     wxService.sendCustomMessage(message); | ||||
|   } | ||||
|    | ||||
|   @Test(dataProvider = "menu", dependsOnMethods = "testRefreshAccessToken") | ||||
|   @Test(dataProvider = "menu", dependsOnMethods = "testRefreshAccessToken", enabled = false) | ||||
|   public void testCreateMenu(WxMenu wxMenu) throws WxErrorException { | ||||
|     wxService.createMenu(wxMenu); | ||||
|   } | ||||
|    | ||||
|   @Test(dependsOnMethods = { "testRefreshAccessToken" , "testCreateMenu"}) | ||||
|   @Test(dependsOnMethods = { "testRefreshAccessToken" , "testCreateMenu"}, enabled = false) | ||||
|   public void testGetMenu() throws WxErrorException { | ||||
|     Assert.assertNotNull(wxService.getMenu()); | ||||
|   } | ||||
|    | ||||
|   @Test(dependsOnMethods = { "testRefreshAccessToken", "testGetMenu" }) | ||||
|   @Test(dependsOnMethods = { "testRefreshAccessToken", "testGetMenu" }, enabled = false) | ||||
|   public void testDeleteMenu() throws WxErrorException { | ||||
|     wxService.deleteMenu(); | ||||
|   } | ||||
|    | ||||
|   @Test | ||||
|   @Test(dependsOnMethods = { "testRefreshAccessToken" }, dataProvider="uploadFiles", 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); | ||||
|     System.out.println(res.toString()); | ||||
|   } | ||||
|    | ||||
|   @DataProvider | ||||
|   public Object[][] uploadFiles() { | ||||
|     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(enabled = false) | ||||
|   public void testCheckSignature() throws WxErrorException { | ||||
|     String timestamp = "23234235423246"; | ||||
|     String nonce = "y7didfkcmvnbd90sdofjkiefhsd"; | ||||
|  | ||||
| @ -3,6 +3,8 @@ package chanjarster.weixin.bean; | ||||
| import org.testng.Assert; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| import chanjarster.weixin.bean.result.WxError; | ||||
|  | ||||
| @Test | ||||
| public class WxErrorTest { | ||||
|  | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								src/test/resources/mm.jpeg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/test/resources/mm.jpeg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 14 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/test/resources/mm.mp3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/test/resources/mm.mp3
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/test/resources/mm.mp4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/test/resources/mm.mp4
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/test/resources/mm.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/test/resources/mm.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 36 KiB | 
		Reference in New Issue
	
	Block a user
	 Daniel Qian
					Daniel Qian