【问题标题】:how to Send UTF-8 character as String over HTTP如何通过 HTTP 将 UTF-8 字符作为字符串发送
【发布时间】:2020-01-22 05:53:31
【问题描述】:

我正在尝试使用 MultipartEntityBuilder 通过 HTTP 发送文件。我们将“文件名”作为字符串属性发送到 addBinaryBody,如下所示。问题是字符串文件名有一些特殊字符,如

“gültig/Kapitel/00/00/SOPs/SOP/sop123.pdf”

但是当它通过 HTTP 时,它就像

“g?ltig/Kapitel/00/00/SOPs/SOP/sop003986.pdf”

我尝试了 URLDecoder 和 new String(bytes, StandardCharsets.UTF_8)。没有任何效果。请提出一些答案。

必答:

特殊字符应为“gültig”而不是“g?ltig”

MultipartEntityBuilder builder = MultipartEntityBuilder.create();
    builder.addTextBody("index", docbase_name.toLowerCase() + "_content_index");
    builder.addBinaryBody("file", fileContent, ContentType.MULTIPART_FORM_DATA,filename);
    HttpEntity multipart = builder.build();
    HttpPost request = new HttpPost(
            "http://" + utility.getIp()
                + ":" + utility.getPort() + "/fscrawler/_upload");
    request.setEntity(multipart);

【问题讨论】:

  • 令人印象深刻的是,由于编码问题,即使最后的数字也会发生变化;-)
  • 哦是的!!.. 我在第二行给出了一个不同的例子:) 但问题是我得到的是“g?ltig”而不是 gültig
  • 我很确定您可以通过指定用于发送数据的编码来解决该问题,具体如何执行取决于您使用的 API,我不知道这一点心。尝试构建自己的 String 肯定是错误的方法。

标签: java utf-8


【解决方案1】:

filename 应该传递给addBinaryBody,编码为 RFC 2047 标头,您可以使用 java mail api 中的 MimeUtility 执行编码(参见例如类似问题 How to put and get UTF-8 string from HTTP header with jersey?):

builder.addBinaryBody("file", fileContent, ContentType.MULTIPART_FORM_DATA, MimeUtility.encodeText(filename));

setEntity 变为:

        request.setEntity(
                MultipartEntityBuilder.create()
                        .addTextBody("index", docbase_name.toLowerCase() + "_content_index")
                        .addBinaryBody("file",
                                fileContent,
                                ContentType.MULTIPART_FORM_DATA,
                                MimeUtility.encodeText(filename))
                        .build());

完整的测试用例:

package com.github.vtitov.test;

import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import org.apache.http.HttpEntity;
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.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.junit.Test;

import javax.mail.internet.MimeUtility;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;

import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;

public class RestTest extends JerseyTest {
    private final static Logger log = Logger.getLogger(MockHttpResource.class.getName());

    @Path("fscrawler")
    public static class FscrawlerResource {
        @POST
        @Consumes("multipart/form-data")
        @Path("_upload")
        public String postToString(final FormDataMultiPart multiPart) throws Exception {
            List<String> fileNames = new LinkedList<>();
            try {
                for(FormDataBodyPart f:multiPart.getFields().get("file")) {
                    fileNames.add(MimeUtility.decodeText(f.getContentDisposition().getFileName()));
                }
            } catch (Exception e) {
                log.log(Level.SEVERE, "server error: ", e);
                throw e;
            }
            return String.join(",", fileNames);
        }
    }

    @Override
    protected Application configure() {
        forceSet(TestProperties.CONTAINER_PORT, "0");
        set(TestProperties.RECORD_LOG_LEVEL, Level.INFO.intValue());
        set(TestProperties.RECORD_LOG_LEVEL, Level.FINE.intValue());
        return new ResourceConfig(FscrawlerResource.class)
                .register(LoggingFeature.class)
                .register(org.glassfish.jersey.media.multipart.MultiPartFeature.class)
                ;
    }

    @Test
    public void multipart() throws IOException {
        String baseUri = target().getUri().toString();
        String docbase_name = UUID.randomUUID().toString();
        byte[] fileContent = UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8);
        String  filename = "gültig/file.txt";

        HttpPost request = new HttpPost(baseUri + "fscrawler/_upload");
        request.setEntity(
                MultipartEntityBuilder.create()
                        .addTextBody("index", docbase_name.toLowerCase() + "_content_index")
                        .addBinaryBody("file",
                                fileContent,
                                ContentType.MULTIPART_FORM_DATA,
                                MimeUtility.encodeText(filename))
                        .build());

        log.info("executing request " + request.getRequestLine());
        try(CloseableHttpClient httpclient = HttpClients.createDefault();
            CloseableHttpResponse response = httpclient.execute(request)
        ) {
            log.info(String.format("response: %s", response.toString()));
            HttpEntity resEntity = response.getEntity();
            assertThat(resEntity, notNullValue());
            if (resEntity != null) {
                log.info("Response content length: " + resEntity.getContentLength());
                String resContent = CharStreams.toString(new InputStreamReader(resEntity.getContent(), Charsets.UTF_8));
                log.info(String.format("response content: %s", resContent));
                assertThat("filename matches", filename, equalTo(resContent));
            }
            EntityUtils.consume(resEntity);
        } catch (IOException e) {
            dumpServerLogRecords();
            throw e;
        }
        dumpServerLogRecords();
    }

    void dumpServerLogRecords() {
        log.info(String.format("total server log records: %s", getLoggedRecords().size()));
        Formatter sf = new SimpleFormatter();
        getLoggedRecords().forEach(r -> {
            log.info(String.format("server log record\n%s", sf.format(r)));
        });

    }
}

您可以启用日志记录以查看请求、响应和处理:

mvn test \
  -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog \
  -Dorg.apache.commons.logging.simplelog.showdatetime=true \
  -Dorg.apache.commons.logging.simplelog.log.org.apache.http=DEBUG \
  -Dorg.apache.commons.logging.simplelog.log.org.apache.http.wire=DEBUG

【讨论】:

  • 我不能使用 MimeUtility 编码,因为我们放在 fscrawler 中的文件名正在被不同的服务器使用。我们无法解码它。我尝试使用new String(filename.getBytes(), StandardCharsets.UTF_8) 但这也不起作用。
  • 我了解到您使用的是fscrawler。不过,尚不清楚您是否可以修补fscrawler 的安装。 Vanilla fscrawler 使用 ISO_8859_1 see here 或者您可以向 github 提交补丁或自己修补您的安装:添加 MimeUtility.decodeMimeUtility.decodeText 到文件名处理
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-31
  • 1970-01-01
  • 1970-01-01
  • 2010-09-21
  • 2016-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多