Jersey Java REST Client调用StreamSets API报500错误,Postman可正常请求
从你提供的错误堆栈来看,服务器端在初始化ZipInputStream时抛出了NullPointerException,说明服务器没有接收到有效的文件输入流,这大概率是你的Jersey客户端构造多部分表单请求时的问题,而非服务器端故障(毕竟Postman请求能成功)。下面是具体的问题点和修复方案:
核心错误点
手动覆盖了
Content-Type头
你手动设置了invocationBuilder.header("Content-type", "multipart/form-data");,但Jersey在使用Entity.entity(formDataMultiPart, MediaType.MULTIPART_FORM_DATA)时,会自动生成包含boundary的正确Content-Type头(比如multipart/form-data; boundary=xxxx)。手动设置会覆盖这个头,导致服务器无法正确解析多部分表单的边界,进而无法读取到上传的文件。FileDataBodyPart的MediaType设置错误
你给FileDataBodyPart指定了MediaType.MULTIPART_FORM_DATA_TYPE,但这个类型是整个请求的媒体类型,单个文件部分应该使用MediaType.APPLICATION_OCTET_STREAM_TYPE,或者让Jersey自动根据文件类型推断。资源释放时机过早
在finally块中,你在读取响应之前就调用了fileDataBodyPart.cleanup()和formDataMultiPart.cleanup(),这可能导致请求还没发送完成,资源就被释放了。
修复后的代码
public static String testUploadService(String httpURL, File filePath) throws Exception { Client client = null; Response response = null; FormDataMultiPart formDataMultiPart = null; String responseString = null; try { // 初始化客户端 ClientConfig cc = new ClientConfig(); cc.register(MultiPartFeature.class); client = new JerseywithSSL().initClient(cc); WebTarget webTarget = client.target(httpURL); // 构造文件上传部分:使用正确的MediaType FileDataBodyPart fileDataBodyPart = new FileDataBodyPart("uploadFile", filePath, MediaType.APPLICATION_OCTET_STREAM_TYPE); formDataMultiPart = new FormDataMultiPart(); formDataMultiPart.bodyPart(fileDataBodyPart); // 构建请求:不要手动设置Content-Type Invocation.Builder invocationBuilder = webTarget.request() .header("Authorization", "Basic " + getBasicAuthString("*******", "*******")) .header("X-Requested-By", "SDC"); // 发送请求,由Jersey自动处理Content-Type(包含boundary) response = invocationBuilder.post(Entity.entity(formDataMultiPart, MediaType.MULTIPART_FORM_DATA)); int responseCode = response.getStatus(); System.out.println("Response code: " + responseCode); if (responseCode != 200) { // 读取错误响应信息,方便排查 String errorResponse = response.readEntity(String.class); System.out.println("Error response: " + errorResponse); throw new RuntimeException("Failed with HTTP error code : " + responseCode + ", details: " + errorResponse); } String responseMessageFromServer = response.getStatusInfo().getReasonPhrase(); System.out.println("ResponseMessageFromServer: " + responseMessageFromServer); responseString = response.readEntity(String.class); } catch (Exception ex) { ex.printStackTrace(); throw ex; } finally { // 确保响应读取完成后再释放资源 if (response != null) { response.close(); } if (formDataMultiPart != null) { formDataMultiPart.close(); } if (client != null) { client.close(); } } return responseString; } // 提取Basic Auth生成逻辑,让代码更清晰 private static String getBasicAuthString(String username, String password) { String authString = username + ":" + password; byte[] encoding = Base64.getEncoder().encode(authString.getBytes()); return new String(encoding); }
为什么Postman能成功?
Postman在发送多部分表单请求时,会自动生成包含boundary的Content-Type头,并且正确设置每个表单部分的媒体类型,服务器可以正常解析文件流。而你的代码因为手动覆盖了关键的请求头,导致服务器无法识别多部分数据的结构,最终拿到null的输入流,触发了NullPointerException。
内容的提问来源于stack exchange,提问作者Vijay Shekhawat

