import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.netty.http.client.HttpClient; import reactor.netty.http.client.HttpClientResponse; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; // 或 HSSFWorkbook import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.UUID; public class ExcelUploader { public static Mono<HttpClientResponse> uploadExcel( Workbook workbook, String originalFilename, String uploadUrl) { // Step 1: 将 Workbook 写入 byte[] byte[] excelBytes; try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { workbook.write(baos); excelBytes = baos.toByteArray(); } catch (IOException e) { return Mono.error(new RuntimeException("Failed to serialize workbook", e)); } // Step 2: 构建 multipart/form-data body String boundary = "----ReactorNettyBoundary" + UUID.randomUUID().toString(); String contentType = "multipart/form-data; boundary=" + boundary; Flux<ByteBuf> body = createMultipartData(boundary, excelBytes, originalFilename); // Step 3: 发送 POST 请求 return HttpClient.create() .post() .uri(uploadUrl) .header("Content-Type", contentType) .send(body) .response(); // 返回响应,可进一步处理 } private static Flux<ByteBuf> createMultipartData(String boundary, byte[] fileBytes, String filename) { String crlf = "\r\n"; String twoHyphens = "--"; // Part 1: 文件头 String fileHeader = twoHyphens + boundary + crlf + "Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"" + crlf + "Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + crlf + crlf; // Part 2: 文件尾 String footer = crlf + twoHyphens + boundary + twoHyphens + crlf; return Flux.concat( // 文件头(文本) Flux.just(fileHeader) .map(s -> Unpooled.wrappedBuffer(s.getBytes(StandardCharsets.UTF_8))), // 文件内容(二进制) Flux.just(Unpooled.wrappedBuffer(fileBytes)), // 结尾 Flux.just(footer) .map(s -> Unpooled.wrappedBuffer(s.getBytes(StandardCharsets.UTF_8))) ); } }