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;
    }

原因

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

builder.setCharset(StandardCharsets.UTF_8);
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

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

源码

//增加参数,我们在传文件和其他参数都会用到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,并传需要的参数

外传

😜 原创不易,如若本文能够帮助到您的同学
🎉 支持我:关注我+点赞👍+收藏⭐️
📝 留言:探讨问题,看到立马回复
💬 格言:己所不欲勿施于人 扬帆起航、游历人生、永不言弃!🔥

Vivado2023是一款集成开发环境软件,用于设计和验证FPGA(现场可编程门阵列)和可编程逻辑器件。对于使用Vivado2023的用户来说,license是必不可少的。 Vivado2023的license是一种许可证,用于授权用户合法使用该软件。许可证分为多种类型,包括评估许可证、开发许可证和节点许可证等。每种许可证都有不同的使用条件和功能。 评估许可证是免费提供的,让用户可以在一段时间内试用Vivado2023的全部功能。用户可以使用这个许可证来了解软件的性能和特点,对于初学者和小规模项目来说是一个很好的选择。但是,使用评估许可证的用户在使用期限过后需要购买正式的许可证才能继续使用软件。 开发许可证是付费的,可以永久使用Vivado2023的全部功能。这种许可证适用于需要长期使用Vivado2023进行开发的用户,通常是专业的FPGA设计师或工程师。购买开发许可证可以享受Vivado2023的技术支持和更新服务,确保软件始终保持最新的版本和功能。 节点许可证是用于多设备或分布式设计的许可证,可以在多个计算机上安装Vivado2023,并共享使用。节点许可证适用于大规模项目或需要多个处理节点进行设计的用户,可以提高工作效率和资源利用率。 总之,Vivado2023 license是用户在使用Vivado2023时必须考虑的问题。用户可以根据自己的需求选择合适的许可证类型,以便获取最佳的软件使用体验。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南巷Dong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值