You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Java 11 HttpClient上传文件到JIRA遇InvalidContentTypeException求助

问题原因

JIRA的附件上传接口要求请求体必须是multipart/form-data格式,而你的代码使用HttpRequest.BodyPublishers.ofFile直接上传文件时,默认会将请求的Content-Type设置为application/octet-stream,不符合接口要求,因此触发了InvalidContentTypeException。

解决方法

需要手动构建符合multipart/form-data格式的请求体,并设置正确的请求头。以下是修改后的完整代码:

1. 新增构建multipart请求体的工具方法

private static HttpRequest.BodyPublisher createMultipartFormData(Path filePath, String boundary) throws IOException {
    // 生成分隔符的字节数组
    byte[] boundaryBytes = ("--" + boundary + "\r\n").getBytes(StandardCharsets.UTF_8);
    // 生成结束分隔符的字节数组
    byte[] endBoundaryBytes = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8);

    // 构建文件部分的头部信息
    String fileContentType = Files.probeContentType(filePath);
    String fileHeader = String.format(
        "Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\nContent-Type: %s\r\n\r\n",
        filePath.getFileName(),
        fileContentType != null ? fileContentType : "application/octet-stream"
    );
    byte[] fileHeaderBytes = fileHeader.getBytes(StandardCharsets.UTF_8);

    // 组合所有部分为请求体
    return HttpRequest.BodyPublishers.ofByteArrays(
        Arrays.asList(
            boundaryBytes,
            fileHeaderBytes,
            Files.readAllBytes(filePath),
            endBoundaryBytes
        )
    );
}

2. 修改postFileNew方法

public static java.net.http.HttpResponse<String> postFileNew(String url, String file, Map<String, String> headers) {
    Path filePath = Paths.get(file);
    HttpRequest request;
    HttpClient httpClient;
    java.net.http.HttpResponse<String> response = null;

    HttpRequest.Builder builder = HttpRequest.newBuilder();
    // 添加请求头时跳过Content-Type,避免和后续设置的冲突
    headers.forEach((key, value) -> {
        if (!"Content-Type".equalsIgnoreCase(key)) {
            builder.header(key, value);
        }
    });

    try {
        // 生成唯一的分隔符
        String boundary = "----JiraUploadBoundary" + System.currentTimeMillis();
        // 构建multipart请求体
        HttpRequest.BodyPublisher bodyPublisher = createMultipartFormData(filePath, boundary);
        
        request = builder
            .uri(URI.create(url))
            // 设置正确的Content-Type,包含分隔符
            .header("Content-Type", "multipart/form-data; boundary=" + boundary)
            .POST(bodyPublisher)
            .build();

        httpClient = getHttpClient(getSslContext());
        response = httpClient.send(request, java.net.http.HttpResponse.BodyHandlers.ofString());

        if (response != null) {
            System.out.println("response status code - " + response.statusCode());
            System.out.println("response body - " + response.body());
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
    return response;
}

3. 调整uploadTestsToJira方法(无需额外修改,确保不添加Content-Type到headers即可)

private static void uploadTestsToJira() throws IOException {
    String fileLocation = "C:\\Users\\username\\git\\project\\src\\main\\resources\\Files\\Hello.txt";
    Map<String, String> headers = new HashMap<>();
    
    String authorizationValue = ParameterStore.getBasicAuthValueFromParameterStore("/proj/projAcct", ",");
    headers.put("Authorization", authorizationValue);
    headers.put("X-Atlassian-Token", "nocheck");
  
    java.net.http.HttpResponse<String> httpResponse = Utilities.postFileNew(
        "https://jira-url/rest/api/2/issue/issue-id/attachments",
        fileLocation, 
        headers
    ); 
}
关键说明
  • multipart/form-data格式需要通过分隔符(boundary)区分请求体中的不同部分,每个文件部分需要包含Content-Disposition(指定字段名和文件名)和Content-Type(文件的MIME类型)。
  • 使用Files.probeContentType可以自动识别文件的MIME类型,提升兼容性;如果识别失败, fallback 到application/octet-stream。
  • 必须确保请求头中的Content-Type包含和请求体一致的boundary值,否则服务器无法正确解析请求。

内容的提问来源于stack exchange,提问作者VinodKhade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 15:12:51