1.使用场景
正在使用企业微疑API交心中,常常开辟者需要使用自界说的资本,好比收收当地图片消息,树立通信录自界说头像等。
为了完毕统一资本文献,一次上传能够屡次使用,那里供给了艳材办理交心:以media_id去标记资本文献,完毕文献的上传取下载。
以收收消息为示例:
以JSSDK选图片上传为示例:
上传的媒介文献限定
统统文献size必需年夜于5个字节
图片(image):10MB,撑持JPG,PNG格局语音(voice) :2MB,播搁少度没有超越60s,仅撑持AMR格局望频(video) :10MB,撑持MP4格局一般文献(file):20MB
HTTP上传文献办法简析
HTTP是文原和谈,若需要通报两退造文献需要依靠于multipart/form-data格局
1.结构 HTTP恳求包
单个文献的multipart/form-data格局,以下:- --分开符[换止]
- Content-Disposition: form-data; name="表单名"; filename="文献名"; filelength=文献实质巨细[换止]
- Content-Type: 范例[换止][换止]
- 文献的两退造实质[换止]--分开符--
复造代码 Content-Type按照差别文献范例能够树立对于应差别的值,以下表格:
文献范例 | Content-Type | 一般文献 | application/octet-stream | jpg图片 | image/jpg | png图片 | image/png | bmp图片 | image/bmp | amr音频 | voice/amr | mp4望频 | video/mp4 | 若咱们树立:分开符为acebdf13572468,文献名为wework.txt,文献实质为mytext,因为上传临时艳材请求name牢固为media,那末机关的恳求实质为:- --acebdf13572468
- Content-Disposition: form-data; name="media";filename="wework.txt"; filelength=6
- Content-Type: application/octet-stream
- mytext
- --acebdf13572468--
复造代码 2. 树立HTTP头部疑息
- POSTURLHTTP/1.1[换止]
- Content-Type: multipart/form-data; boundary=分开符[换止]
- Content-Length: 恳求体实质巨细[换止][换止]
- 第1步机关的恳求体实质
复造代码 假设咱们将第1步组拆的文献实质上传到企业微疑临时艳材,分开符与第1步设定值acebdf13572468,那末便获得以下:- POSThttps://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=accesstoken001&type=file HTTP/1.1
- Content-Type: multipart/form-data; boundary=acebdf13572468
- Content-Length:168--acebdf13572468
- Content-Disposition: form-data; name="media";filename="wework.txt"; filelength=6
- Content-Type: application/octet-stream
- mytext
- --acebdf13572468--
复造代码 上传临时艳材
艳材上传获得media_id,该media_id仅三天内乱有用
media_id正在统一企业内乱使用之间能够同享
**恳求方法:**POST(HTTPS)
**恳求地点:**https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
使用multipart/form-data POST上传文献, 文献标记名为"media"
参数分析:
参数 | 必需 | 分析 | access_token | 是 | 挪用交心凭据 | type | 是 | 媒介文献范例,别离有图片(image)、语音(voice)、望频(video),一般文献(file) | POST的恳求包中,form-data中媒介文献标记,应包罗有 filename、filelength、content-type等疑息
filename标记文献展示的称呼。好比,使用该media_id收消息时,展示的文献名由该字段掌握
恳求示例:- POSThttps://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=accesstoken001&type=file HTTP/1.1
- Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
- Content-Length:220---------------------------acebdf13572468
- Content-Disposition: form-data; name="media";filename="wework.txt"; filelength=6
- Content-Type: application/octet-stream
- mytext
- ---------------------------acebdf13572468--
复造代码 前去数据:- {"errcode":0,"errmsg":"",
- "type":"image","media_id":"1G6nrLmr5EC3MMb_-zK1dDdzmd0p7cNliYu9V5w7o8K0","created_at":"1380000000"}
复造代码 参数分析:
参数 | 分析 | type | 媒介文献范例,别离有图片(image)、语音(voice)、望频(video),一般文献(file) | media_id | 媒介文献上传后获得的唯一标记,3天内乱有用 | created_at | 媒介文献上传时间戳 | 上传企微临时艳材,对于应企微api文档链交:https://developer.work.weixin.qq.com/document/path/90253
2.掌握层交心
掌握层领受方法能够有文献方法领受,也能够前端用图片变换成base64格局字符串方法后端领受。
文献方法领受
- @RequestMapping(method =RequestMethod.POST, value ="v1/uploadQwMedia")publicScaResponseParam<JSONObject>uploadQwMedia(String type,String title,int orgId,@RequestParam("file")MultipartFile file){try{if(file ==null){thrownewIllegalArgumentException("不上传图片");}if(StringUtils.isEmpty(type)){thrownewIllegalArgumentException("缺少type参数");}InputStream inputStream = file.getInputStream();FindMediaIdReq req =newFindMediaIdReq();
- req.setTitle(title);//对于应企微api文档中的fileName参数,用于掌握展示的文献称呼
- req.setType(type);//对于应企微api文档中的type参数String mediaId = scaGuideOneCustOneCodeService.uploadQwMedia(orgId, req, inputStream);returnScaResponseParam.OK().fluentSetData(newJSONObject().fluentPut("mediaId", mediaId));}catch(IllegalArgumentException e){
- log.error(e.getMessage(), e);returnScaResponseParam.ERROR(e.getMessage());}catch(Exception e){
- log.error("上传客户博属码到企微临时艳材非常:{}-{}",e.getMessage(), e);returnScaResponseParam.ERROR("上传客户博属码到企微临时艳材非常");}}
复造代码 base64方法领受
- @RequestMapping(value ="/base64ImgUpload", method =RequestMethod.POST)publicScaResponseParam<JSONObject>base64ImgUpload(@RequestBodyOneCustOneCodeUploadQwImgRequest request){try{BASE64Decoder decoder =newBASE64Decoder();byte[] bytes = decoder.decodeBuffer(request.getBase64Str());for(int i =0; i < bytes.length;++i){if(bytes[i]<0){
- bytes[i]+=256;}}int orgId = request.getOrgId();InputStream inputStream =newByteArrayInputStream(bytes);FindMediaIdReq req =newFindMediaIdReq();
- req.setTitle(request.getTitle());//对于应企微api文档中的fileName参数,用于掌握展示的文献称呼
- req.setType(request.getType());//对于应企微api文档中的type参数String mediaId = scaGuideOneCustOneCodeService.uploadQwMedia(orgId, req, inputStream);returnScaResponseParam.OK().fluentSetData(newJSONObject().fluentPut("mediaId", mediaId));}catch(IllegalArgumentException e){
- log.error(e.getMessage(), e);returnScaResponseParam.ERROR(e.getMessage());}catch(Exception e){
- log.error("base64上传客户博属码到企微临时艳材非常:{}-{}",e.getMessage(), e);returnScaResponseParam.ERROR("base64上传客户博属码到企微临时艳材非常");}}
复造代码 恳求参数OneCustOneCodeUploadQwImgRequest类- @Data@SuppressWarnings("all")publicclassOneCustOneCodeUploadQwImgRequest{@ApiModelProperty(value ="文献范例,图片为image", required =true)privateString type;@ApiModelProperty(value ="文献称呼", required =true)privateString title;@ApiModelProperty(value ="图片base64格局字符串", required =true)privateString base64Str;@ApiModelProperty(value ="orgId", required =true)privateint orgId;}
复造代码 4.Service层交心
- @OverridepublicStringuploadQwMedia(Integer orgId,FindMediaIdReq reqBean,InputStream inputStream){Map<String,Object> map = qyWeiXinService.uploadMediaForKf(orgId, reqBean, inputStream);String mediaId = map.get("media_id").toString();String createdAt =DateUtil.date2Str(newDate(Long.valueOf(map.get("created_at").toString())*1000));
- log.info("上传企微艳材前去,mediaId:{},createAt:{}", mediaId, createdAt);return mediaId;}
复造代码 qyWeiXinService中的uploadMediaForKf办法- @OverridepublicMap<String,Object>uploadMediaForKf(Integer orgId,FindMediaIdReq reqBean,InputStream inputStream){ScaQyWxBuConfig scaQyWxBuConfig =ScaQyWxBuConfig.getConfigByOrgId(orgId);//获得设置的secret战corpId等疑息if(scaQyWxBuConfig ==null){
- log.error("客服企微参数设置为空, orgId:{}", orgId);thrownewRuntimeException("获得客服企微设置参数失利");}String accessToken =this.getKfAccessToken(scaQyWxBuConfig);//获得accessTokenString title = reqBean.getTitle();//挪用企微api上传图片文献到企微临时艳材returnWinXinMessageUtil.mediaUploadByInputStream(accessToken, inputStream, reqBean.getType(), title);}
复造代码 挪用企微api上传图片文献到企微临时艳材办法,对于应上面的WinXinMessageUtil.mediaUploadByInputStream办法- publicstaticMap<String,Object>mediaUploadByInputStream(String accessToken,InputStream inputStream,String type,String fileName){String upUrl ="https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token="+ accessToken +"&type="+ type;StringBuffer buffer =newStringBuffer();BufferedReader reader =null;try{URL urlObj =newURL(upUrl);HttpURLConnection con =(HttpURLConnection) urlObj.openConnection();
- con.setRequestMethod("POST");// 以Post方法提接表单,默认get方法
- con.setDoInput(true);
- con.setDoOutput(true);
- con.setUseCaches(false);// post方法不克不及使用慢存// 树立恳求头疑息
- con.setRequestProperty("Connection","Keep-Alive");
- con.setRequestProperty("Charset","UTF-8");// 树立鸿沟StringBOUNDARY="----------"+System.currentTimeMillis();
- con.setRequestProperty("Content-Type","multipart/form-data; boundary="+BOUNDARY);// 恳求正文疑息StringBuilder sb =newStringBuilder();
- sb.append("--");//必需 多二讲线
- sb.append(BOUNDARY);
- sb.append("\r\n");
- sb.append("Content-Disposition: form-data;name="media";filename=""+ fileName +""\r\n");
- sb.append("Content-Type:application/octet-stream\r\n\r\n");byte[] head = sb.toString().getBytes("utf-8");//取得 输出流OutputStream out =newDataOutputStream(con.getOutputStream());//输出 表头
- out.write(head);// 把文献已经流文献的方法 拉进到url中DataInputStream in =newDataInputStream(inputStream);int bytes;byte[] bufferOut =newbyte[1024];while((bytes = in.read(bufferOut))!=-1){
- out.write(bufferOut,0, bytes);}
- in.close();//开头 部门byte[] foot =("\r\n--"+BOUNDARY+"--\r\n").getBytes("utf-8");// 界说最初数据分开线
- out.write(foot);
- out.flush();
- out.close();// 界说BufferedReader输出流去读与URL的照应InputStream conInputStream = con.getInputStream();
- reader =newBufferedReader(newInputStreamReader(conInputStream));String line;while((line = reader.readLine())!=null){
- buffer.append(line);}String result = buffer.toString();
- log.warn("{}上传临时艳材成果:{}", fileName, result);Map<String,Object> map =JSON.parseObject(result,Map.class);if(!Objects.equals(map.get("errcode"),0)){thrownewIllegalArgumentException("上传临时艳材非常:"+ map.get("errmsg"));}return map;}catch(IOException e){
- log.warn("上传临时艳材{}非常:{}-{}", fileName, e.getMessage(), e);thrownewIllegalArgumentException("上传临时艳材非常", e);}finally{try{if(reader !=null){
- reader.close();}}catch(IOException e){
- e.printStackTrace();}}}
复造代码 获得token的办法,对于应上面的getKfAccessToken办法
那里先从redis慢存获得,获得没有到再挪用企微api交心获得,能够按照理论情况截至通融。- privateStringgetKfAccessToken(ScaQyWxBuConfig scaQyWxBuConfig){String corpId = scaQyWxBuConfig.getCorpId();//从设置中获得的corpIdString secret = scaQyWxBuConfig.getSecretKf();//从设置中获得的secretif(StringUtils.isEmpty(corpId)||StringUtils.isEmpty(secret)){returnnull;}String key ="HYP_GUIDE_"+ corpId +"AccessToken"+ secret;//劣先从redis慢存中获得String accessToken = jedisCluster.get(key);if(StringUtils.isEmpty(accessToken)){//慢存中获得没有到再挪用企微api交心获得accessTokentry{
- accessToken =WinXinMessageUtil.getAccessToken(corpId, secret);
- jedisCluster.set(key, accessToken);
- jedisCluster.expire(key,7000);//树立过时时间}catch(Exception e){
- log.error(e.getMessage());}}return accessToken;}
复造代码 3.挪用企微api交心获得accessToken疑息,
对于应上面的WinXinMessageUtil.getAccessToken- publicstaticStringgetAccessToken(StringCorpID,StringSecret)throwsException{String access_token ="";CloseableHttpClient httpclient =HttpClients.createDefault();try{HttpGet httpGet =newHttpGet("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="+CorpID+"&corpsecret="+Secret);CloseableHttpResponse response1 = httpclient.execute(httpGet);JSONObject resultJsonObject;try{HttpEntity httpEntity = response1.getEntity();if(httpEntity !=null){try{BufferedReader bufferedReader =newBufferedReader(newInputStreamReader(httpEntity.getContent(),"UTF-8"),8*1024);StringBuilder entityStringBuilder =newStringBuilder();String line;while((line = bufferedReader.readLine())!=null){
- entityStringBuilder.append(line);}//使用 从HttpEntity中获得的String天生JsonObject
- resultJsonObject =newJSONObject(entityStringBuilder.toString().trim());
- access_token = resultJsonObject.get("access_token")+"";}catch(Exception e){
- log.warn("获得企微token非常:{}-{}", e.getMessage(), e);}}}finally{
- response1.close();}}finally{
- httpclient.close();}return access_token;}
复造代码 |