回顾
第一步:平台序列号获取
开始图片上传
微信官方图片上传文档 阅读文档可知
- 请求的url: api.mch.weixin.qq.com/secapi/mch/…
- 提交方式: multipart/form-data
- 是否需要证书: 是
参数了解
- mch_id : 商户号
- media : 媒体文件(form-data中媒体文件标识,有filename、filelength、content-type等信息。不参与签名计算)
- media_hash : 媒体文件内容hash值(根据媒体文件内容进行MD5计算后的值,注意小写)
- sign : 签名
- sign_type : 签名方式( HMAC-SHA256加密方式,其他或者不填为MD5方式)
证书获取
微信官方证书使用
最终有个apiclient_cert.p12 文件
呃。。。。。。。。。先丢个pom文件,防止童鞋们乱找一通
xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.ligroupId>
<artifactId>micro-merchantartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.8.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.6version>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpmimeartifactId>
<version>4.5.3version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.18version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.1version>
dependency>
<dependency>
<groupId>dom4jgroupId>
<artifactId>dom4jartifactId>
<version>1.6.1version>
dependency>
<dependency>
<groupId>jaxengroupId>
<artifactId>jaxenartifactId>
<version>1.1.6version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>${java.version}source>
<target>${java.version}target>
configuration>
plugin>
plugins>
build>
project>
复制代码
代码:
package com.li.utils;
import org.apache.http.ssl.SSLContexts;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
public class SSLContextUtils {
/**
* 获取证书内容
* @param certPath 证书地址
* @param mchId 商户号
* @return
*/
public static SSLContext getSSLContext(String certPath, String mchId) {
InputStream inputStream;
try {
inputStream = new FileInputStream(new File(certPath));
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
KeyStore keystore = KeyStore.getInstance("PKCS12");
char[] partnerId2charArray = mchId.toCharArray();
keystore.load(inputStream, partnerId2charArray);
return SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build();
} catch (Exception var9) {
throw new RuntimeException("证书文件有问题,请核实!", var9);
} finally {
IOUtils.closeQuietly(inputStream);
}
}
}
复制代码
- certPath : 证书地址
- mchId : 商户号
第二步 httpClient上传图片至微信官方
package com.li.upload.service.impl;
import com.li.upload.service.UploadService;
import com.li.utils.SSLContextUtils;
import com.li.utils.SignUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Service
@Slf4j
public class UploadServiceImpl implements UploadService{
@Override
public String uploadFile(MultipartFile multipartFile) {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/mch/uploadmedia");
httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.MULTIPART_FORM_DATA.getMimeType());
CloseableHttpClient client = null;
File excelFile = null;
String error = null;
try {
client = HttpClients.custom().setSSLContext(SSLContextUtils.getSSLContext("证书地址","商户号")).build();
// 生成签名和图片md5加密
String hash = DigestUtils.md5Hex(multipartFile.getBytes());
Map param = new HashMap<>(3);
param.put("media_hash", hash);
param.put("mch_id", "商户号");
param.put("sign_type", "HMAC-SHA256");
// 配置post图片上传
// 用uuid作为文件名,防止生成的临时文件重复
excelFile = File.createTempFile(UUID.randomUUID().toString(), ".jpg");
multipartFile.transferTo(excelFile);
FileBody bin = new FileBody(excelFile, ContentType.create("image/jpg", Consts.UTF_8));
HttpEntity build = MultipartEntityBuilder.create().setCharset(Charset.forName("utf-8"))
.addTextBody("media_hash", hash)
.addTextBody("mch_id", "商户号")
.addTextBody("sign_type", "HMAC-SHA256")
.addTextBody("sign", SignUtil.wechatCertficatesSignBySHA256(param, "api密钥"))
.addPart("media", bin)
.build();
httpPost.setEntity(build);
HttpResponse httpResponse = client.execute(httpPost);
if (httpResponse != null && httpResponse.getStatusLine().getStatusCode() == 200) {
String responseEntity = EntityUtils.toString(httpResponse.getEntity());
log.info("upload response {}", responseEntity);
Document document = DocumentHelper.parseText(responseEntity);
if ("SUCCESS".equalsIgnoreCase(document.selectSingleNode("//return_code").getStringValue())) {
if ("SUCCESS".equalsIgnoreCase(document.selectSingleNode("//result_code").getStringValue())) {
return document.selectSingleNode("//media_id").getStringValue();
}
}
log.error("上传图片失败,异常信息 code ={} des = {}", document.selectSingleNode("//err_code").getStringValue(), document.selectSingleNode("//err_code_de").getStringValue());
error = document.selectSingleNode("//err_code_de").getStringValue();
}
} catch (Exception e) {
log.error("微信图片上传异常 , e={}", e);
} finally {
if (client != null) {
try {
client.close();
} catch (IOException e) {
log.warn("关闭资源httpclient失败 {}", e);
}
}
if (excelFile != null) {
deleteFile(excelFile);
}
}
return error;
}
/**
* 删除临时文件
*
* @param files
*/
private void deleteFile(File... files) {
for (File file : files) {
if (file.exists()) {
file.delete();
}
}
}
}
复制代码
第三步,编写controller
package com.li.upload.controller;
import com.li.upload.service.UploadService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* 小微商户
*/
@Slf4j
@RequestMapping("/microMerchant")
@RestController
public class MicroMerchantUploadController {
@Autowired
private UploadService uploadService;
/**
* 上传小微商户图片
*
* @param multipartFile
* @return
*/
@PostMapping("/upload")
public String upload(@RequestParam(value = "file", required = false) MultipartFile multipartFile) {
log.info("图片大小 {}", multipartFile.getSize());
return uploadService.uploadFile(multipartFile);
}
}
复制代码
第四步 测试
你可以使用postman测试,我这边写了个单元测试进行测试
package com.li.upload;
import com.li.upload.service.UploadService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UploadServiceImplTest {
@Autowired
private UploadService uploadService;
@Test
public void uploadFile() throws Exception {
File file = new File("图片路径");
MultipartFile multipartFile = new MockMultipartFile("file", new FileInputStream(file));
String s = uploadService.uploadFile(multipartFile);
System.out.println(s);
}
}
复制代码
返回结果
<xml><return_code>return_code>
<return_msg>return_msg>
<result_code>result_code>
<media_id>media_id>
<sign>sign>
xml>
复制代码
最终得到media_id
源代码
github.com/bertonlee/m… 分支为upload
如果帮助到您,欢迎star
个人博客地址
www.ccode.live/bertonlee/l…