如何在Java中使用HttpClient获取文件上传进度并跨类调用
Absolutely, you can track file upload progress using a wrapper class for HttpEntity and a listener pattern to share that progress with other classes. Let's break this down step by step, adjusting your existing code to handle file uploads and add progress tracking.
Step 1: Define a Progress Listener Interface
First, create an interface so other classes can subscribe to progress updates. This decouples the progress tracking logic from your HttpClient code:
public interface UploadProgressListener { void onProgressUpdate(long bytesUploaded, long totalBytes); }
Step 2: Create a Progress-Tracking Entity Wrapper
We'll wrap the HttpEntity (used for file uploads) to intercept the write operation and calculate progress. This class extends HttpEntityWrapper to override the write behavior:
import org.apache.http.HttpEntity; import org.apache.http.entity.HttpEntityWrapper; import java.io.IOException; import java.io.OutputStream; public class ProgressTrackingEntity extends HttpEntityWrapper { private final UploadProgressListener listener; public ProgressTrackingEntity(HttpEntity entity, UploadProgressListener listener) { super(entity); this.listener = listener; } @Override public void writeTo(OutputStream outstream) throws IOException { super.writeTo(new CountingOutputStream(outstream, listener, getContentLength())); } private static class CountingOutputStream extends OutputStream { private final OutputStream delegate; private final UploadProgressListener listener; private final long totalBytes; private long bytesWritten = 0; public CountingOutputStream(OutputStream delegate, UploadProgressListener listener, long totalBytes) { this.delegate = delegate; this.listener = listener; this.totalBytes = totalBytes; } @Override public void write(int b) throws IOException { delegate.write(b); bytesWritten++; listener.onProgressUpdate(bytesWritten, totalBytes); } @Override public void write(byte[] b) throws IOException { delegate.write(b); bytesWritten += b.length; listener.onProgressUpdate(bytesWritten, totalBytes); } @Override public void write(byte[] b, int off, int len) throws IOException { delegate.write(b, off, len); bytesWritten += len; listener.onProgressUpdate(bytesWritten, totalBytes); } @Override public void flush() throws IOException { delegate.flush(); } @Override public void close() throws IOException { delegate.close(); } } }
Step 3: Modify Your HttpClient Code for File Uploads & Progress Tracking
Your original code uses StringEntity for JSON payloads. For file uploads, we'll use MultipartEntityBuilder to create a multipart form, then wrap it with our ProgressTrackingEntity. Here's the updated method:
import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.io.File; import java.io.IOException; import java.util.Map; import java.util.Map.Entry; public static SusResponseDTO uploadFileRequest(String url, File fileToUpload, Map<String, String> requestHeaders, UploadProgressListener progressListener) { CloseableHttpClient httpClient = null; HttpPost httpPost = null; CloseableHttpResponse response = null; SusResponseDTO susResponseDTO = null; try { httpClient = HttpClients.createDefault(); httpPost = new HttpPost(url); // Build multipart entity for file upload MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); entityBuilder.addBinaryBody( "file", // Name of the file parameter expected by your server fileToUpload, ContentType.DEFAULT_BINARY, fileToUpload.getName() ); HttpEntity originalEntity = entityBuilder.build(); // Wrap the entity with our progress tracker HttpEntity progressTrackingEntity = new ProgressTrackingEntity(originalEntity, progressListener); httpPost.setEntity(progressTrackingEntity); // Add request headers for (final Entry<String, String> h : requestHeaders.entrySet()) { httpPost.addHeader(h.getKey(), h.getValue()); } // Execute the upload request response = httpClient.execute(httpPost); if (response.getStatusLine().getStatusCode() != 200) { throw new SusException(FAILED_HTTP_ERROR_CODE + response.getStatusLine().getStatusCode()); } // Parse response using your existing logic for (org.apache.http.Header header : response.getAllHeaders()) { if (header.getName().equals(SERVER) && header.getValue().contains(MASTER_SERVER)) { susResponseDTO = JsonUtils.jsonStreamToObject(response.getEntity().getContent(), SusResponseDTO.class); break; } } if (susResponseDTO == null) { HttpEntity entity = response.getEntity(); String responseString = EntityUtils.toString(entity, DEFAULT_ENCODING); Map<String, String> map = JsonUtils.jsonToMap(responseString, Map.class); susResponseDTO = JsonUtils.jsonToObject(map.get(RESPONSE_ENTITY), SusResponseDTO.class); } } catch (final IOException e) { ExceptionLogger.logException(e, SuSClient.class); throw new SusException(e, SuSClient.class); } finally { IOUtils.safeClose(response); IOUtils.safeClose(httpClient); } return susResponseDTO; }
Step 4: Use the Listener in Another Class
Any class can implement UploadProgressListener to receive real-time progress updates. For example, a monitoring class or UI component:
public class UploadProgressMonitor implements UploadProgressListener { @Override public void onProgressUpdate(long bytesUploaded, long totalBytes) { double progressPercentage = (bytesUploaded * 100.0) / totalBytes; System.out.printf("Upload Progress: %.2f%% (%d/%d bytes)%n", progressPercentage, bytesUploaded, totalBytes); // Here you can update UI elements, log progress to a file, or trigger other business logic // If updating UI, make sure to run on the UI thread (e.g., SwingUtilities.invokeLater() or Android's runOnUiThread()) } }
To use it, pass an instance of your listener when calling the upload method:
File targetFile = new File("/path/to/your/file.pdf"); Map<String, String> headers = Map.of("Authorization", "Bearer your-auth-token"); UploadProgressMonitor monitor = new UploadProgressMonitor(); SusResponseDTO uploadResponse = uploadFileRequest("https://your-upload-api.com/upload", targetFile, headers, monitor);
Key Notes
- Thread Safety: If you're updating UI components from the listener, ensure those updates run on the UI thread to avoid concurrency issues.
- Reusability: The
ProgressTrackingEntityandUploadProgressListenerare generic, so you can reuse them across all your file upload methods. - Error Handling: The listener tracks bytes written, but you still need to handle exceptions in the main HttpClient method as you already do.
内容的提问来源于stack exchange,提问作者Noman Arshad

