• HttpClient MultipartEntityBuilder中文乱码问题解决


    1. 问题

    最近使用httpclient上传图片并伴有中文其他字段参数,然后,传输过去后,中文显示???乱码问题

    2. 解决后代码

    
    //调用方法
    public static ResultHik createVisitor(String accessToken, MdVisitor mdVisitor) throws DeviceException {
        byte[] faceVerify = mdVisitor.getVerifyFace();
        mdVisitor.setVerifyFace(null);
        log.info("调用魔点api: 创建访客授权 init accessToken:{};mdVisitor:{}", accessToken, JSON.toJSONString(mdVisitor));
        String uri = MdConstant.createVisitor(accessToken);
    
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
    
        //重点:::::之前没有,解决问题的点,需要增加需要传中文的格式设置UTF-8格式
        ContentType contentType = ContentType.create("multipart/form-data",StandardCharsets.UTF_8);
    
        builder.setContentType(contentType);
        //识别人脸图片
        builder.addBinaryBody("verifyFace", faceVerify, ContentType.MULTIPART_FORM_DATA, mdVisitor.getVisitorMobile() + ".jpg");
        //传中文
        builder.addTextBody("visitorName", mdVisitor.getVisitorName(), contentType);
        builder.addTextBody("visitorMobile", StringUtils.leftPad(mdVisitor.getVisitorMobile(), 11, "1"), ContentType.MULTIPART_FORM_DATA);
        builder.addTextBody("startTime", JSON.toJSONString(mdVisitor.getStartTime()), ContentType.MULTIPART_FORM_DATA);
        builder.addTextBody("endTime", JSON.toJSONString(mdVisitor.getEndTime()), ContentType.MULTIPART_FORM_DATA);
        //传中文
        builder.addTextBody("address", JSON.toJSONString(mdVisitor.getAddress()), contentType);
        builder.addTextBody("ids", StringUtils.join(mdVisitor.getPermitDeviceIds(), ","), ContentType.MULTIPART_FORM_DATA);
        String resCtx = HttpHelper.doUploadImageHttp(RequestType.POST, uri, builder);
        ResultHik bean = new Gson().fromJson(resCtx, new TypeToken<ResultHik>() {}.getType());
        return bean;
    }
    
    //执行方法
     public static String sendUploadHttp(RequestType reqType, String url, Map<String, String> headers,
                                            MultipartEntityBuilder builder) throws DeviceException {
            HttpRequestBase reqBase = reqType.getHttpType(url);
            log.info("--->>开始向地址[{}]发起 [{}] 上传图片请求", url, reqBase.getMethod());
            log.info("--->>请求头为{}", JSON.toJSONString(headers));
            log.info("--->>请求参数为{}", JSON.toJSONString(builder));
            long startTime = System.currentTimeMillis();
            CloseableHttpClient httpClient = getHttpClient();
            //设置请求url
            config(reqBase);
    
            //已经设置了,防止中文乱码,但是为什么还有乱码问题
            builder.setCharset(StandardCharsets.UTF_8);
            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
            HttpEntity build = builder.build();
            ((HttpEntityEnclosingRequest) reqBase).setEntity(build);
            //响应对象
            CloseableHttpResponse res = null;
            //响应内容
            String resCtx = null;
            try {
                //执行请求
                res = httpClient.execute(reqBase);
                log.info("--->>执行请求完毕,响应状态:{}", res.getStatusLine());
                if (res.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                    throw new DeviceException("--->>HTTP访问异常:" + res.getStatusLine());
                }
                //获取请求响应对象和响应entity
                HttpEntity httpEntity = res.getEntity();
                if (httpEntity != null) {
                    resCtx = EntityUtils.toString(httpEntity, "utf-8");
                    log.info("--->>获取响应内容:{}", resCtx);
                }
            } catch (Exception e) {
                throw new DeviceException("请求失败", e);
            } finally {
                if (res != null) {
                    try {
                        res.close();
                    } catch (IOException e) {
                        throw new DeviceException("--->>关闭请求响应失败", e);
                    }
                }
            }
            long endTime = System.currentTimeMillis();
            log.info("--->>请求执行完毕,耗费时长:{} 秒", (endTime - startTime) / 1000);
            return resCtx;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    原因

    其实在执行代码中已经有了解决中文乱码问题,

    builder.setCharset(StandardCharsets.UTF_8);
    builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    
    
    • 1
    • 2
    • 3

    那为什么还是会乱码,咱么一起看看源码

    源码

    //增加参数,我们在传文件和其他参数都会用到ContentType.MULTIPART_FORM_DATA
    builder.addTextBody("address", JSON.toJSONString(mdVisitor.getAddress()), ContentType.MULTIPART_FORM_DATA);
    //MultipartEntityBuilder类
    //addTextBody方法
    public MultipartEntityBuilder addTextBody(
                final String name, final String text, final ContentType contentType) {
            return addPart(name, new StringBody(text, contentType));
        }
    //去看看ContentType的MULTIPART_FORM_DATA怎么处理的
    //ContentType类,用的是ISO_8859_1格式,就是这个问题
    public static final ContentType MULTIPART_FORM_DATA = create("multipart/form-data", Consts.ISO_8859_1);
    
    //我们再去看下MultipartEntityBuilder类buildEntity方法
    
        MultipartFormEntity buildEntity() {
            String boundaryCopy = boundary;
            if (boundaryCopy == null && contentType != null) {
                boundaryCopy = contentType.getParameter("boundary");
            }
            if (boundaryCopy == null) {
                boundaryCopy = generateBoundary();
            }
            Charset charsetCopy = charset;
            if (charsetCopy == null && contentType != null) {
                charsetCopy = contentType.getCharset();
            }
            final List<NameValuePair> paramsList = new ArrayList<NameValuePair>(2);
            paramsList.add(new BasicNameValuePair("boundary", boundaryCopy));
            if (charsetCopy != null) {
                paramsList.add(new BasicNameValuePair("charset", charsetCopy.name()));
            }
            final NameValuePair[] params = paramsList.toArray(new NameValuePair[paramsList.size()]);
            final ContentType contentTypeCopy = contentType != null ?
                    contentType.withParameters(params) :
                    ContentType.create("multipart/" + DEFAULT_SUBTYPE, params);
            //参数获取
            final List<FormBodyPart> bodyPartsCopy = bodyParts != null ? new ArrayList<FormBodyPart>(bodyParts) :
                    Collections.<FormBodyPart>emptyList();
            final HttpMultipartMode modeCopy = mode != null ? mode : HttpMultipartMode.STRICT;
            final AbstractMultipartForm form;
            switch (modeCopy) {
                case BROWSER_COMPATIBLE:
                    //mode我们使用的是这个模式,我们传入的参数bodyPartsCopy
                    form = new HttpBrowserCompatibleMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
                    break;
                case RFC6532:
                    form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, bodyPartsCopy);
                    break;
                default:
                    form = new HttpStrictMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
            }
            return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength());
        }
    //在看下HttpBrowserCompatibleMultipart对bodyPartsCopy的处理
        public HttpBrowserCompatibleMultipart(
                final Charset charset,
                final String boundary,
                final List<FormBodyPart> parts) {
            super(charset, boundary);
            //直接使用传入的参数
            this.parts = parts;
        }
    //最后,我们使用的参数还是格式,
    //所以关键的地方,要自己设置ContentType,并传需要的参数
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    外传

    😜 原创不易,如若本文能够帮助到您的同学
    🎉 支持我:关注我+点赞👍+收藏⭐️
    📝 留言:探讨问题,看到立马回复
    💬 格言:己所不欲勿施于人 扬帆起航、游历人生、永不言弃!🔥
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    华为OD机试 - 最差产品奖 - 双端队列 deque(Java 2023 B卷 200分)
    【Unity3D】获取UGUI位置不正确问题
    Linux应用程序和驱动程序接口
    vue请求代理查看真实地址
    ElasticSearch查询工具类分享
    drools执行完某个规则后终止别的规则执行
    Vue+elementUI 导出word打印
    温故知新(十一)——IIC
    MySQL深度剖析及面试秘籍(必知必会30题全)
    软件测试面试中90%会遇到的问题:“你会搭建测试环境吗?”
  • 原文地址:https://blog.csdn.net/fclwd/article/details/134552337