收藏
评论

v3图片上传接口java代码(HttpURLConnection实现)

折腾了一天,终于把这个图片接口(https://api.mch.weixin.qq.com/v3/merchant/media/upload)调通了。看到社区里面有不少小伙伴遇到了一样的问题,这里简单分享一下我的测试代码。因为我是用java开发的,所以下面内容的代码示例都是java的。

 水平有限,如有错漏之处,请多多指教。

public class uploadFileTest {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
             
        try {
              
            // 换行符
            String LINE_END = "\r\n";
            String PREFIX = "--";
            // 定义数据分隔线
            String BOUNDARY = "";
             
            //商户号
            String mchid = "";
            //证书序列号
            String serial_no = "";
            //商户私钥(拷贝apiclient_key.pem文件里-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----之间的内容)
            String rsaPrivateKeyFile = "";
            //微信支付平台公钥文件
            String rsaPublicKey = "";
             
            //时间戳
            String timestamp = Long.toString(System.currentTimeMillis()/1000);
            //随机数
            String nonce_str = "";
             
            //图片文件
            String filePath = "";//文件路径
            File file = new File(filePath);
            String filename = file.getName();//文件名
            String fileSha256 = DigestUtils.sha256Hex(new FileInputStream(file));//文件sha256值
             
            //拼签名串
            StringBuffer sb = new StringBuffer();
            sb.append("POST").append("\n");
            sb.append("/v3/merchant/media/upload").append("\n");
            sb.append(timestamp).append("\n");
            sb.append(nonce_str).append("\n");
            sb.append("{\"filename\":\""+filename+"\",\"sha256\":\""+fileSha256+"\"}").append("\n");
            System.out.println("签名原串:"+sb.toString());
             
            //计算签名
            String sign = new String(Base64.encodeBase64(signRSA(sb.toString(),rsaPrivateKey)));
            System.out.println("签名sign值:"+sign);
             
            //拼装http头的Authorization内容
            String authorization ="WECHATPAY2-SHA256-RSA2048 mchid=\""+mchid+"\",nonce_str=\""+nonce_str+"\",signature=\""+sign+"\",timestamp=\""+timestamp+"\",serial_no=\""+serial_no+"\"";
            System.out.println("authorization值:"+authorization);
             
            //接口URL
            URL url = new URL("https://api.mch.weixin.qq.com/v3/merchant/media/upload");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 设置为POST
            conn.setRequestMethod("POST");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求头参数
            conn.setRequestProperty("Charsert", "UTF-8");
            conn.setRequestProperty("Accept","application/json");
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
            conn.setRequestProperty("Authorization", authorization);
  
            DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
  
            //拼装请求内容第一部分
            StringBuilder strSb = new StringBuilder();
            strSb.append(PREFIX).append(BOUNDARY).append(LINE_END)
                 .append("Content-Disposition: form-data; name=\"meta\";" + LINE_END)
                 .append("Content-Type: application/json; " + LINE_END)
                 .append(LINE_END)// 空行
                 .append("{\"filename\":\""+filename+"\",\"sha256\":\""+fileSha256+"\"}")
                 .append(LINE_END);

           dos.write(strSb.toString().getBytes());

            dos.flush();
             
            //拼装请求内容第二部分
            StringBuilder fileSbStart = new StringBuilder();
            fileSbStart.append(PREFIX).append(BOUNDARY).append(LINE_END)
                       .append("Content-Disposition: form-data; name=\"file\"; filename=\""+ filename+ "\";" + LINE_END)
                       .append("Content-Type: image/jpeg" + LINE_END)
                       .append(LINE_END);// 空行

           dos.write(fileSbStart.toString().getBytes());

            dos.flush();
             
            //文件二进制内容
            InputStream is = new FileInputStream(file);
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = is.read(buffer)) != -1){
                dos.write(buffer,0,len);
            }
            is.close();
             
            //拼装请求内容结尾
            StringBuilder fileSbEnd = new StringBuilder();
            fileSbEnd.append(LINE_END)
                     .append(PREFIX).append(BOUNDARY).append(PREFIX)
                     .append(LINE_END);
            

           dos.write(fileSbEnd.toString().getBytes());

            dos.flush();
            dos.close();
  
            //接收返回
            //打印返回头信息
            System.out.println("接口返回头信息:");
            Map<String, List<String>> responseHeader = conn.getHeaderFields();
            for (Map.Entry<String, List<String>> entry : responseHeader.entrySet()) {
                System.out.println(entry.getKey()+":" + entry.getValue());
             }
             
            //打印返回内容
            int responseCode = conn.getResponseCode();
            String rescontent = "";
            if((responseCode+"").startsWith("2")){
                //成功
                rescontent = new String(InputStreamTOByte(conn.getInputStream()));
                System.out.println("图片上传成功:"+rescontent);
            }else{
                //失败
                rescontent = new String(InputStreamTOByte(conn.getErrorStream()));
                System.out.println("图片上传失败:"+rescontent);
            }
             
            //验证微信支付返回签名
            String Wtimestamp = responseHeader.get("Wechatpay-Timestamp").get(0);
            String Wnonce = responseHeader.get("Wechatpay-Nonce").get(0);
            String Wsign = responseHeader.get("Wechatpay-Signature").get(0);
            //拼装待签名串
            StringBuffer ss = new StringBuffer();
            ss.append(Wtimestamp).append("\n");
            ss.append(Wnonce).append("\n");
            ss.append(rescontent).append("\n");
            //验证签名
            if(verifyRSA(ss.toString(), Base64.decodeBase64(Wsign.getBytes()), rsaPublicKeyFile)) {
                System.out.println("签名验证成功");
            } else {
                System.out.println("签名验证失败");
            }
  
        } catch (Exception e) {
            System.out.println("发送POST请求异常!" + e);
            e.printStackTrace();
        }
 
    }
     
    
 
}


最后一次编辑于  2019-09-17
收藏

14 个评论

  • null
    null
    12-04

    可以使用httpClient 实现文件流的传输吗?

    12-04
    赞同
    回复
  • Uncle·Xiang
    Uncle·Xiang
    2021-07-01

    图片上传失败:{"code":"PARAM_ERROR","message":"商户没有权限"}

    上传图片的接口功能也要去微信官方开通权限吗?

    2021-07-01
    赞同
    回复
  • 科技学院
    科技学院
    2020-09-16

    上传成功后怎么或者到客户端上显示呢?https://pay.weixin.qq.com/files/media/get?media_id=xxxxx。获取不到啊,需要验证吗

    2020-09-16
    赞同
    回复
  • 路人甲
    路人甲
    2019-11-18

    我用的这套代码为什么签名错误?我私钥没问题呀

    {

    "code": "SIGN_ERROR",

    "detail": {

    "detail": {

    "issue": "sign not match"

    },

    "field": "signature",

    "location": "authorization",

    "sign_information": {

    "method": "POST",

    "sign_message_length": 175,

    "truncated_sign_message": "POST\n/v3/merchant/media/upload\n1574050808\n3dbf238498594cec83ab1e9f9fee45e8\n{\"filena\n",

    "url": "/v3/merchant/media/upload"

    }

    },

    "message": "错误的签名,验签失败"

    }


    2019-11-18
    赞同
    回复 3
    • 阿杰
      阿杰
      2019-12-10
      我也是这种情况怎么解决的
      2019-12-10
      回复
    • 路人甲
      路人甲
      2019-12-16回复阿杰
      密钥错了,重新下载密钥看看
      2019-12-16
      回复
    • 无缘彼岸
      无缘彼岸
      2020-05-18
      参与签名计算的请求主体为meta的json串:
      { "filename": "filea.jpg", "sha256": "hjkahkjsjkfsjk78687dhjahdajhk" }
      2020-05-18
      回复
  • test
    test
    2019-09-17

    修改了一下签名和验签的方法

    public static byte[] InputStreamTOByte(InputStream in) throws IOException{ 
             
            int BUFFER_SIZE = 4096
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            byte[] data = new byte[BUFFER_SIZE]; 
            int count = -1
             
            while((count = in.read(data,0,BUFFER_SIZE)) != -1
                outStream.write(data, 0, count); 
               
            data = null
            byte[] outByte = outStream.toByteArray();
            outStream.close();
             
            return outByte; 
        }
         
        public static byte[] signRSA(String data, String priKey) throws Exception {
     
            //签名的类型
            Signature sign = Signature.getInstance("SHA256withRSA");
            //读取商户私钥,该方法传入商户私钥证书的内容即可
            byte[] keyBytes = Base64.decodeBase64(priKey);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            sign.initSign(privateKey);
            sign.update(data.getBytes("UTF-8"));
            return sign.sign();
         }
         
        public static boolean verifyRSA(String data, byte[] sign, String pubKey) throws Exception{
            if(data == null || sign == null || pubKey == null){
                return false;
            }
     
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            FileInputStream in = new FileInputStream(pubKey);
            Certificate c = cf.generateCertificate(in);
            in.close();
            PublicKey publicKey = c.getPublicKey();
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initVerify(publicKey);
            signature.update(data.getBytes("UTF-8"));
            return signature.verify(sign);
     
        }


    2019-09-17
    赞同
    回复
  • 阿狸的移动城堡
    阿狸的移动城堡
    2019-09-09

    --接口文档1:获取商户号授权状态接口

    https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/applysubject/chapter5_4.shtml


    --接口文档2:提交申请单API

    https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/applysubject/chapter5_1.shtml


    --接口文档3:撤销申请单API

    https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/applysubject/chapter5_2.shtml


    --接口文档4:查询申请单审核结果API

    https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/applysubject/chapter5_3.shtml


    --接口文档5:获取平台证书列表

    https://wechatpay-api.gitbook.io/wechatpay-api-v3/jie-kou-wen-dang/ping-tai-zheng-shu


    --接口文档6:图片上传

    https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/download/图片上传.pdf


    --重点信息:


    --开发过程密钥、apiv3、jdk、微信提供的httpClient jar

    https://wechatpay-api.gitbook.io/wechatpay-api-v3/ 


    --微信提供的jar 自用需要修改一些部分

    https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient


    --test 大佬实现的地址

    --解释

    https://developers.weixin.qq.com/community/develop/doc/000ca6c41e8e581c3f1932f3051404

    --httpClient 4.5.9的实现

    https://developers.weixin.qq.com/community/develop/doc/000ae8e8904a38ac4019bb24651404


    2019-09-09
    赞同
    回复 3
    • 青蛙哎呀
      青蛙哎呀
      2019-09-09
      成功了。感谢
      2019-09-09
      回复
    • 杨小瘦
      杨小瘦
      2019-09-09回复青蛙哎呀
      熊得,怎么解决的,难受
      2019-09-09
      回复
    • 青蛙哎呀
      青蛙哎呀
      2019-09-10回复杨小瘦
      下面能不能帮到你
      2019-09-10
      回复
  • 阿狸的移动城堡
    阿狸的移动城堡
    2019-09-09


    2019-09-09
    赞同
    回复
  • 青蛙哎呀
    青蛙哎呀
    2019-09-09

    更新了JDK,但是还是不行,读出来的秘钥全是0,第一次接接口,弄疯了,谢谢你


    2019-09-09
    赞同
    回复 4
    • 阿狸的移动城堡
      阿狸的移动城堡
      2019-09-09
      微信有提供jar 包的 --认证、开发工具地址 https://wechatpay-api.gitbook.io/wechatpay-api-v3/
      2019-09-09
      回复
    • 阿狸的移动城堡
      阿狸的移动城堡
      2019-09-09
      建议代码弄下来改改
      2019-09-09
      回复
    • 阿狸的移动城堡
      阿狸的移动城堡
      2019-09-09
      看我下面的回复,你直接取 需要替换掉一些东西的 亲
      2019-09-09
      回复
    • 阿狸的移动城堡
      阿狸的移动城堡
      2019-09-09
      地址刚刚贴错了,这个是: https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient
      2019-09-09
      回复
  • 青蛙哎呀
    青蛙哎呀
    2019-09-09


    你好我的代码总是报错,说秘钥格式不对,我是从这个路径加载秘钥的,谢谢。


    2019-09-09
    赞同
    回复 5
    • 阿狸的移动城堡
      阿狸的移动城堡
      2019-09-09
      你的JDK 啥版本的 注意要 1.8.0.151 以上的 然后设置无限制参数加密 https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/api-v3-mi-yao-xiang-guan
      2019-09-09
      回复
    • 晓
      2019-09-09
      不知道你解决了吗? 我跟你一样的问题
      2019-09-09
      回复
    • 青蛙哎呀
      青蛙哎呀
      2019-09-09回复
      我直接从证书里面取私钥,然后提醒我验签失败
      2019-09-09
      回复
    • 青蛙哎呀
      青蛙哎呀
      2019-09-09回复阿狸的移动城堡

      更新了JDK,但是还是不行,读出来的秘钥全是0,第一次接接口,弄疯了,谢谢你

      2019-09-09
      回复
    • 晓
      2019-09-09
      我也是一样的啊 看了好多资料 都不行 我都快.............
      2019-09-09
      回复
  • 阿狸的移动城堡
    阿狸的移动城堡
    2019-09-06

    请求报文里面送的:

    网站上加出来的:

    微信返回的:


    JDK已经换成:1.8.0.221 版本了

    2019-09-06
    赞同
    回复 2
    • test
      test
      2019-09-06
      String BOUNDARY ="";这里你设置了没有?你要设置一个自定义的字符串,数字加字母的
      2019-09-06
      回复
    • 阿狸的移动城堡
      阿狸的移动城堡
      2019-09-06回复test
      设置的空串儿 写上 值 以后单元测试可以了
      2019-09-06
      回复

正在加载...

登录 后发表内容