当前位置: 首页 > 知识库问答 >
问题:

“内容处置”的“文件名”中的UTF-8字符产生“IllegalArgumentException:意外字符”

淳于禄
2023-03-14

是否可以从okhttp3客户端发送UTF-8字符?

对于以下字符串:

String fileName = "3$ Mù F'RANçé_33902_Country_5_202105";
String contentDisposition = "attachment;filename=" + "\"" +  fileName + "\"";

我已经尝试过(对于contentDissition标头):

Headers headers = new Headers.Builder()
                       .addUnsafeNonAscii("Content-Disposition", contentDisposition)
                       .add("Authorization", bearer)
                       .add("Content-type", "application/octet-stream")
                       .build();
             Request request = new Request.Builder()
                     .headers(headers)
                     .post(requestBody) 
                     .url(urlAddress)
                     .build();

但服务器收到:3$MÃF'RANçÃ33902_Country_5_202105

此请求发送给公司合作伙伴,因此我无法访问后端。

应用程序/八位流是后端需要的。

身体是这样产生的:

byte[] data = FileUtils.readFileToByteArray(file);
RequestBody requestBody = RequestBody.create(data);

这对邮递员来说非常好。

完整的MVCE(不能包含文件和后端信息,但它以前崩溃过,所以您可以直接启动这段代码,它应该会抛出错误):

public class App 
{
    public static void main( String[] args ) throws IOException
    {
                OkHttpClient client = new OkHttpClient().newBuilder()
                    .build();
                MediaType mediaType = MediaType.parse("application/octet-stream");
                RequestBody body = RequestBody.create(mediaType, "");
                Request request = new Request.Builder()
                  .url("xxxx")
                  .method("POST", body)
                  .addHeader("Content-Type", "application/octet-stream")
                  .addHeader("content-disposition", "attachment;filename=\"3$ Mù F'RANçé_33902_Country_5_202105.csv\"")
                  .addHeader("Authorization", "Bearer xxxxx")
                  .addHeader("Cookie", "xxxxxx")
                  .build();
                Response response = client.newCall(request).execute();
    }
}

收到错误:java。lang.IllegalArgumentException:内容处置值:附件中25处的意外字符0xf9;filename=“3$MùF'RANç33902_Country_5_202105.csv”

okhttp版本:5.0.0-alpha。2

我错过什么了吗?

谢啦

共有1个答案

柴昆杰
2023-03-14

HTTP头的默认字符集是ISO-8859-1。然而,RFC 6266描述了如何在内容处置标题中对文件名进行编码。基本上,您可以指定字符集名称,然后对UTF-8字符进行百分比编码。使用以fileName*=utf-8''开头的参数,而不是fileName=“my simple fileName”,比如

import java.net.URLEncoder;

// ...

String fileName = "3$ Mù F'RANçé_33902_Country_5_202105";
String contentDisposition = "attachment;filename*=utf-8''" + encodeFileName(fileName);

// ...

private static String encodeFileName(String fileName) throws UnsupportedEncodingException {
  return URLEncoder.encode(fileName, "UTF-8").replace("+", "%20");
}

如果想避免使用Guava、Spring的ContentDisposition类或任何其他库,只需使用JRE类,那么使用URL编码器然后修改“”的结果是我在这里发现的一个廉价技巧。

更新:这是一个完整的MCVE,展示了如何将UTF-8字符串作为帖子正文和内容处置文件名发送。演示服务器展示了如何手动解码该报头——通常HTTP服务器应该自动解码。

Maven 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.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>SO_Java_OkHttp3SendUtf8_70804280</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp</artifactId>
      <version>4.9.3</version>
    </dependency>
    <dependency>
      <groupId>org.nanohttpd</groupId>
      <artifactId>nanohttpd</artifactId>
      <version>2.3.1</version>
    </dependency>
  </dependencies>

</project>

OkHttp演示客户端:

import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

public class Client {
  public static void main(String[] args) throws IOException {
    String fileName = "3$ Mù F'RANçé_33902_Country_5_202105";
    String contentDisposition = "attachment;filename*=utf-8''" + encodeFileName(fileName);
    RequestBody requestBody = RequestBody.create(fileName.getBytes(StandardCharsets.UTF_8));
    Headers headers = new Headers.Builder()
      .add("Content-Disposition", contentDisposition)
      .add("Content-type", "application/octet-stream; charset=utf-8")
      .build();
    Request request = new Request.Builder()
      .headers(headers)
      .post(requestBody)
      .url(new URL("http://localhost:8080/"))
      .build();
    OkHttpClient client = new OkHttpClient();
    Response response = client.newCall(request).execute();
    System.out.println(Objects.requireNonNull(response.body()).string());
  }

  private static String encodeFileName(String fileName) {
    return URLEncoder.encode(fileName, StandardCharsets.UTF_8).replace("+", "%20");
  }
}

NanoHTTPD演示服务器:

import fi.iki.elonen.NanoHTTPD;

import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class Server extends NanoHTTPD {

  public Server() throws IOException {
    super(8080);
    start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
    System.out.println("\nRunning! Point your browsers to http://localhost:8080/ \n");
  }

  public static void main(String[] args) throws IOException {
    new Server();
  }

  private static final String UTF_8_FILE_NAME_PREFIX = ";filename*=utf-8''";
  private static final int UTF_8_FILE_NAME_PREFIX_LENGTH = UTF_8_FILE_NAME_PREFIX.length();

  @Override
  public Response serve(IHTTPSession session) {
    try {
      Map<String, String> files = new HashMap<>();
      session.parseBody(files);
      String postBody = files.get("postData");
      String contentDisposition = session.getHeaders().get("content-disposition");
      String fileName = decodeFileName(
        contentDisposition.substring(
          contentDisposition.indexOf(UTF_8_FILE_NAME_PREFIX) + UTF_8_FILE_NAME_PREFIX_LENGTH
        )
      );
      System.out.println("POST body:           " + postBody);
      System.out.println("Content disposition: " + contentDisposition);
      System.out.println("UTF-8 file name:     " + fileName);
      return newFixedLengthResponse(postBody + "\n" + fileName);
    }
    catch (IOException | ResponseException e) {
      e.printStackTrace();
      return newFixedLengthResponse(e.toString());
    }
  }

  private static String decodeFileName(String fileName) {
    return URLDecoder.decode(fileName.replace("%20", "+"), StandardCharsets.UTF_8);
  }

}

如果先运行服务器,然后运行客户端,您将在服务器控制台上看到:

Running! Point your browsers to http://localhost:8080/ 

POST body:           3$ Mù F'RANçé_33902_Country_5_202105
Content disposition: attachment;filename*=utf-8''3%24%20M%C3%B9%20F%27RAN%C3%A7%C3%A9_33902_Country_5_202105
UTF-8 file name:     3$ Mù F'RANçé_33902_Country_5_202105

在客户端控制台上,您会看到:

3$ Mù F'RANçé_33902_Country_5_202105
3$ Mù F'RANçé_33902_Country_5_202105

 类似资料:
  • 我正在Ubuntu中运行httpd+mod_jk+2个tomcat服务器。当我以非拉丁语言提交表单时,我会在DB中得到垃圾。如果我通过Tomcat提交相同的表单,直接绕过httpd,那么一切看起来都很好。以下是我的配置: /ETC/APACHE2/CONF.D/CHARSET: Tomcat1: Tomcat2: JDBC连接: jdbc:mysql://localhost:3306/myapp?

  • 在Flutter中,无论我尝试解码什么json文件,我都会得到上面相同的标题错误。最初我认为这是我的项目,但在开始一个新的模板Flutter项目后,我仍然收到同样的错误。我有json文件在我的根文件夹,并已将它们添加到pubspec.yaml文件: 主要的目前的dart代码: 我已经在多个测试站点验证了我的json数据,并在Flutter文档中尝试了各种方法。Json数据:

  • 我试图反序列化JSON的格式 类类型 因为我有一个映射类型,所以我添加了一个键反序列化器来反序列化 但是当我尝试反序列化时,我得到了一个异常 这是因为它遇到了在中,它在那里中断。如果我将整个键放在双引号中,它会将整个键传递给我的反序列化程序。 有没有一种方法,我可以迫使它转义char,而不必把整个关键与引号。

  • 问题内容: 我在字符串列表的json.Marshal上得到这个: 原因很明显,但是如何在Go中删除/替换这样的字符串?我一直在阅读docst 和包,似乎没有明显/快速的方法。 例如,在Python中,您可以使用一些方法删除无效字符,将其替换为指定字符或严格设置,这会导致无效字符的异常。如何在Go中做等效的事情? 更新:我的意思是得到异常的原因(紧急?)-json.Marshal期望有效的UTF-8

  • 问题内容: 网络服务器使用utf-8编码提供响应,所有文件都使用utf-8编码保存,我所知的所有设置都已设置为utf-8编码。 这是一个快速程序,用于测试输出是否有效: 该程序的输出为: 呈现为: 我可能做错了什么?我必须告诉DomDocument正确处理utf-8的具体程度是多少? 问题答案: 需要一个HTML字符串。 HTML 根据其规范使用默认的编码(ISO拉丁字母1号)。那是因为更长,请参

  • 问题内容: 我想测试我的代码的Unicode处理。有什么我可以放入random.choice()从整个Unicode范围中选择的东西,最好不是外部模块吗?Google和StackOverflow似乎都没有答案。 编辑:看起来这比预期的要复杂,所以我将重新表述这个问题- 以下代码足以生成Unicode中的所有有效非控制字符吗? 问题答案: 您可以使用Markus Kuhn 提供的UTF-8压力测试。