为什么写这篇博客?
领导要求公众号自动回复需要支持图片(之前的版本只支持文本和图文),而支持图片就需要先把图片上传到微信素材库,于是我就开始读文档,查资料,发现还真不好找。尤其是步骤2中上传的参数问题——我试着把前端的MultipartFile转为InputStream再转InputStreamBody,但是失败了。如果直接转File再转FileBody的话,可以上传,但是就比较复杂,微信必须先把MultipartFile保存为本地的File,然后再把这个File读取出来,这就有点恶心了。于是我又找到了一种两全的办法MultipartFile转InputStream再转byte[],这样就方便了很多。话说多了......直接上源码----》》》
1.Controller层(从前端接收要上传的文件)
package com.wxpub.controller.enterprise;
import com.youliantong.wxpub.config.Resources;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Arrays;
/**
* @Description 图片上传controller
* @Author BigBird
* @Date 2018/11/27 19:15
**/
@RestController("fileController")
@RequestMapping("/enterprise")
@Slf4j
public class FileController {
// 上传微信永久素材的url
private static final String UPLOAD_FOREVER_MEDIA_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=ACCESS_TOKEN&type=TYPE";
private static final long MAX_SIZE = 1024 * 1024 * 2;
private static final String SUPPORTED_FORMAT = "bmp,png,jpeg,jpg,gif";
/**
* 上传素材到微信素材库
* @param file
*/
@RequestMapping(value = "uploadMediaToWechat", method = RequestMethod.POST)
@ResponseBody
public String uploadMediaToWechat(@RequestParam("media") @ApiParam(name = "media", value = "待上传素材图片文件", required = true) MultipartFile file) throws Exception{
String[] split = file.getOriginalFilename().split("\\.");
String suffixName = split[split.length-1];
boolean validPic = this.isValidPic(file.getSize(), suffixName);
if (!validPic) {
return "上传失败";
}
// 获取access_token,如果你不会获取,那么这篇博客不适合你。
// 这步在你的IDE上会提示报错,请修改为你自己获取access_token的方式
String token = TokenUtil.getToken(account);
String replacedUrl = UPLOAD_FOREVER_MEDIA_URL
.replace("ACCESS_TOKEN", token)
.replace("TYPE", "image");
JSONObject jsonObject = CommonUtil.uploadFile(replacedUrl,
file.getResource().getInputStream(),file.getOriginalFilename());
log.info("微信素材上传结果:[{}]", jsonObject.toString());
if (jsonObject != null && jsonObject.containsKey("media_id")) {
return jsonObject.getString("media_id") ;
}
return "素材图片上传失败";
}
/**
* 图片是否符合微信规范
* @param size
* @param suffixName
* @return
*/
public Boolean isValidPic(long size, String suffixName) {
if (size > MAX_SIZE) {
log.info("文件太大,文件的大小最大为2M,请重新上传!");
return false;
}
if (!Arrays.asList(SUPPORTED_FORMAT.split(",")).contains(suffixName)) {
log.info("图片格式不支持,请选择bmp/png/jpeg/jpg/gif的任意一种!");
return false;
}
return true;
}
}
2.把文件上传到微信服务器
package com.wxpub.util;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
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 org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.*;
import java.net.ConnectException;
import java.net.URL;
/**
* http通用工具类
*
*/
public class CommonUtil {
private static Logger log = LoggerFactory.getLogger(CommonUtil.class);
/**
* 把文件上传到指定url上去
* @param url 上传地址
* @param file 待上传文件
*/
public static JSONObject uploadFile(String url, InputStream file,String fileName) throws IOException{
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpPost httppost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(200000).setSocketTimeout(200000).build();
httppost.setConfig(requestConfig);
/**
* 这一步最关键:
*调用addBinaryBody("media",input2byte(file),ContentType.DEFAULT_BINARY, fileName),
* fileName可以为任意值,但不能为null,如果为null则上传失败。
* input2byte(file):把inputstream转为byte[],
* 如果直接调用addPart用FileBody做参数,则MultifilePart不好转换;
* 如果直接调用addPart用InpustreamBody做参数,则因为没有fileName会造成上传失败
*/
HttpEntity reqEntity = MultipartEntityBuilder.create()
.addBinaryBody("media", input2byte(file), ContentType.DEFAULT_BINARY,
fileName).build();
httppost.setEntity(reqEntity);
CloseableHttpResponse response = httpclient.execute(httppost);
try {
System.out.println(response.getStatusLine());
HttpEntity resEntity = response.getEntity();
if (resEntity != null) {
String responseEntityStr = EntityUtils.toString(response.getEntity());
log.info("responseEntityStr=[{}]",responseEntityStr);
return JSONObject.fromObject(responseEntityStr);
}
EntityUtils.consume(resEntity);
} finally {
response.close();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
private static final byte[] input2byte(InputStream inStream)
throws IOException {
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[100];
int rc = 0;
while ((rc = inStream.read(buff, 0, 100)) > 0) {
swapStream.write(buff, 0, rc);
}
byte[] in2b = swapStream.toByteArray();
return in2b;
}
}