使用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
相关产品推荐
相关产品推荐

