Apache HttpClient获取Azure Blob时出现ConnectionClosedException求助
Apache HttpClient获取Azure Blob内容时抛出ConnectionClosedException异常
使用Apache HttpClient获取HTTP响应,已成功获取响应头,但读取响应内容时抛出ConnectionClosedException,异常提示内容长度不匹配(预期42320字节,实际仅收到7787字节)。
异常栈信息
Caused by: org.apache.http.ConnectionClosedException: Premature end of Content-Length delimited message body (expected: 42 320; received: 7 787) [INFO] [talledLocalContainer] at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:178) ~[httpcore-4.4.15.jar:4.4.15] [INFO] [talledLocalContainer] at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:135) ~[httpclient-4.5.13.jar:4.5.13] [INFO] [talledLocalContainer] at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:148) ~[httpclient-4.5.13.jar:4.5.13] [INFO] [talledLocalContainer] at com.blueway.common.utils.BWFileUtils.writeToFile(BWFileUtils.java:207) ~[engine-module-common-0.0.1-SNAPSHOT.jar:?] [INFO] [talledLocalContainer] at com.blueway.platform.app.engine.technicalconnector.impl.service.azureblobstorage.AzureBlobStorageService.getBlob(AzureBlobStorageService.java:49) ~[web-0.0.1-SNAPSHOT.jar:?] [INFO] [talledLocalContainer] at com.blueway.platform.app.engine.technicalconnector.impl.service.azureblobstorage.invoker.AzureBlobStorageGetBlobInvoker.invoke(AzureBlobStorageGetBlobInvoker.java:22) ~[web-0.0.1-SNAPSHOT.jar:?] [INFO] [talledLocalContainer] at com.blueway.platform.app.engine.technicalconnector.impl.service.azureblobstorage.AzureBlobStorageInvokeWrapper.doInvoke(AzureBlobStorageInvokeWrapper.java:34) ~[web-0.0.1-SNAPSHOT.jar:?] [INFO] [talledLocalContainer] at com.blueway.engine52.support.ConnectorAzureBlobStorage.invoke(ConnectorAzureBlobStorage.java:46) ~[web-0.0.1-SNAPSHOT.jar:?] [INFO] [talledLocalContainer] at com.blueway.engine52.service.instruction.ConnectorHandlingInstruction.invokeConnector(ConnectorHandlingInstruction.java:68) ~[web-0.0.1-SNAPSHOT.jar:?] [INFO] [talledLocalContainer] at com.blueway.engine52.service.instruction.ConnectorHandlingInstruction.executeInstructionWithSupport(ConnectorHandlingInstruction.java:50) ~[web-0.0.1-SNAPSHOT.jar:?] [INFO] [talledLocalContainer] at com.blueway.engine52.service.instruction.ConnectorHandlingInstruction.executeInstructionWithSupport(ConnectorHandlingInstruction.java:19) ~[web-0.0.1-SNAPSHOT.jar:?] [INFO] [talledLocalContainer] at com.blueway.engine52.service.instruction.SupportHandlingInstruction.executeInstruction(SupportHandlingInstruction.java:99) ~[web-0.0.1-SNAPSHOT.jar:?] [INFO] [talledLocalContainer] at com.blueway.engine52.service.instruction.Instruction.tryExecuteInstruction(Instruction.java:363) ~[web-0.0.1-SNAPSHOT.jar:?]
问题代码
public CloseableHttpResponse get(final String containerName, final String blobName) throws AzBException{ final Logger logger = LoggerFactory.getLogger(getClass()); final String url = HTTPS + accountName + END_POINT + containerName + "/" + blobName; try (final CloseableHttpClient httpClient = HttpClients.createDefault()) { final HttpGet httpGet = new HttpGet(url); logger.debug("Sending HTTP GET request to URL: {}", url); final CloseableHttpResponse response = httpClient.execute(httpGet); logger.debug("Received HTTP response with status code: {}", response.getStatusLine().getStatusCode()); return response; } catch (IOException e) { throw new AzBException("An.get HttpGet error: " + e.getCause().getMessage(), e); } }
解决方案
问题根源
代码使用try-with-resources创建CloseableHttpClient,当方法返回CloseableHttpResponse时,HttpClient已经被自动关闭,导致后续读取响应流时连接提前断开,无法获取完整的响应内容。
修复方案1:复用HttpClient(推荐)
HttpClient是线程安全的,建议将其作为类级别的成员变量全局复用,避免每次请求都创建和关闭客户端:
private final CloseableHttpClient httpClient = HttpClients.createDefault(); private final Logger logger = LoggerFactory.getLogger(getClass()); public CloseableHttpResponse get(final String containerName, final String blobName) throws AzBException{ final String url = HTTPS + accountName + END_POINT + containerName + "/" + blobName; try { final HttpGet httpGet = new HttpGet(url); logger.debug("Sending HTTP GET request to URL: {}", url); final CloseableHttpResponse response = httpClient.execute(httpGet); logger.debug("Received HTTP response with status code: {}", response.getStatusLine().getStatusCode()); return response; } catch (IOException e) { throw new AzBException("An.get HttpGet error: " + e.getMessage(), e); } } // 应用关闭时手动释放HttpClient资源 @PreDestroy public void closeHttpClient() throws IOException { httpClient.close(); }
修复方案2:在方法内完成响应内容读取
如果不需要返回响应对象,可以在方法内部直接读取响应内容,使用try-with-resources同时管理HttpClient和响应对象,确保资源正确释放:
public byte[] get(final String containerName, final String blobName) throws AzBException{ final Logger logger = LoggerFactory.getLogger(getClass()); final String url = HTTPS + accountName + END_POINT + containerName + "/" + blobName; try (final CloseableHttpClient httpClient = HttpClients.createDefault(); final CloseableHttpResponse response = httpClient.execute(new HttpGet(url))) { logger.debug("Received HTTP response with status code: {}", response.getStatusLine().getStatusCode()); // 读取响应内容为字节数组,EntityUtils会自动处理流关闭 return EntityUtils.toByteArray(response.getEntity()); } catch (IOException e) { throw new AzBException("An.get HttpGet error: " + e.getMessage(), e); } }
如果需要将内容写入文件,可直接在try块内完成写入操作,避免传递未处理的响应流:
public void getBlobToFile(final String containerName, final String blobName, File targetFile) throws AzBException{ final Logger logger = LoggerFactory.getLogger(getClass()); final String url = HTTPS + accountName + END_POINT + containerName + "/" + blobName; try (final CloseableHttpClient httpClient = HttpClients.createDefault(); final CloseableHttpResponse response = httpClient.execute(new HttpGet(url)); final InputStream inputStream = response.getEntity().getContent()) { logger.debug("Received HTTP response with status code: {}", response.getStatusLine().getStatusCode()); // 直接写入文件 BWFileUtils.writeToFile(inputStream, targetFile); } catch (IOException e) { throw new AzBException("Failed to get blob to file: " + e.getMessage(), e); } }
内容的提问来源于stack exchange,提问作者IphoneVore
相关产品推荐
相关产品推荐

