0
# Multi-Part Processing
1
2
Multi-part content handling for form data, file uploads, and byte ranges with async processing support, compliance validation, and streaming capabilities.
3
4
## Capabilities
5
6
### MultiPart Class
7
8
Namespace class for multi-part content processing with parsing, generation, and boundary utilities.
9
10
```java { .api }
11
/**
12
* Namespace class for multi-part content support
13
*/
14
class MultiPart {
15
/** Extract boundary parameter from Content-Type header */
16
static String extractBoundary(String contentType);
17
18
/** Generate multipart boundary with prefix and random characters */
19
static String generateBoundary(String prefix, int randomLength);
20
21
/**
22
* Individual part within multi-part content
23
*/
24
abstract static class Part implements Content.Source.Factory, Closeable {
25
/** Get part name from Content-Disposition header */
26
String getName();
27
28
/** Get filename from Content-Disposition header */
29
String getFileName();
30
31
/** Get part headers */
32
HttpFields getHeaders();
33
34
/** Get part content as source */
35
Content.Source getContentSource();
36
37
/** Get part content length */
38
long getLength();
39
40
/** Get part content type */
41
String getContentType();
42
43
/** Check if part represents a file upload */
44
boolean isFile();
45
46
/** Close part and release resources */
47
void close();
48
}
49
50
/**
51
* Multi-part content parser
52
*/
53
static class Parser {
54
/** Create parser with boundary and buffer pool */
55
Parser(String boundary, ByteBufferPool bufferPool);
56
57
/** Parse content source into parts */
58
void parse(Content.Source source, PartHandler handler);
59
60
/** Interface for handling parsed parts */
61
interface PartHandler {
62
/** Handle parsed part */
63
void handle(Part part);
64
}
65
}
66
}
67
```
68
69
### MultiPartFormData Class
70
71
Specialized multi-part processing for form data with async parsing.
72
73
```java { .api }
74
/**
75
* Multi-part form data processing with async support
76
*/
77
class MultiPartFormData {
78
/** Create form data processor with default configuration */
79
MultiPartFormData();
80
81
/** Create with custom configuration */
82
MultiPartFormData(MultiPartConfig config);
83
84
/** Parse multi-part content asynchronously */
85
CompletableFuture<Parts> parse(Content.Source source);
86
87
/** Parse multi-part content synchronously (blocking) */
88
Parts from(Content.Source source);
89
90
/** Parse with content type header */
91
CompletableFuture<Parts> parse(Content.Source source, String contentType);
92
93
/** Get maximum part size */
94
long getMaxPartSize();
95
96
/** Set maximum part size */
97
void setMaxPartSize(long maxPartSize);
98
99
/** Get maximum file size for uploads */
100
long getMaxFileSize();
101
102
/** Set maximum file size for uploads */
103
void setMaxFileSize(long maxFileSize);
104
105
/** Get maximum request size */
106
long getMaxRequestSize();
107
108
/** Set maximum request size */
109
void setMaxRequestSize(long maxRequestSize);
110
111
/**
112
* Collection of parsed multi-part form parts
113
*/
114
interface Parts extends Iterable<MultiPart.Part> {
115
/** Get part by name */
116
MultiPart.Part get(String name);
117
118
/** Get all parts with given name */
119
List<MultiPart.Part> getAll(String name);
120
121
/** Get part names */
122
Set<String> getNames();
123
124
/** Get all parts as list */
125
List<MultiPart.Part> asList();
126
127
/** Get number of parts */
128
int size();
129
130
/** Check if empty */
131
boolean isEmpty();
132
133
/** Release all parts */
134
void release();
135
}
136
}
137
```
138
139
### MultiPartByteRanges Class
140
141
Multi-part byte range processing for HTTP Range requests.
142
143
```java { .api }
144
/**
145
* Multi-part byte ranges for HTTP Range responses
146
*/
147
class MultiPartByteRanges {
148
/** Create byte ranges with content type */
149
MultiPartByteRanges(String contentType);
150
151
/** Add byte range part */
152
void addPart(ByteRange range, Content.Source content);
153
154
/** Get boundary string */
155
String getBoundary();
156
157
/** Get content type with boundary */
158
String getContentType();
159
160
/** Get parts as content source */
161
Content.Source getContentSource();
162
163
/** Get total content length */
164
long getContentLength();
165
}
166
```
167
168
### MultiPartConfig Class
169
170
Configuration for multi-part processing with size limits and temp directory settings.
171
172
```java { .api }
173
/**
174
* Configuration for multi-part processing
175
*/
176
class MultiPartConfig {
177
/** Create configuration with default settings */
178
MultiPartConfig();
179
180
/** Create with size limits */
181
MultiPartConfig(long maxFileSize, long maxRequestSize, int fileSizeThreshold);
182
183
/** Create with full configuration */
184
MultiPartConfig(long maxFileSize, long maxRequestSize,
185
int fileSizeThreshold, String location);
186
187
/** Get maximum file size for individual uploads */
188
long getMaxFileSize();
189
190
/** Get maximum total request size */
191
long getMaxRequestSize();
192
193
/** Get file size threshold for writing to disk */
194
int getFileSizeThreshold();
195
196
/** Get temporary file location */
197
String getLocation();
198
}
199
```
200
201
### MultiPartCompliance Class
202
203
Multi-part compliance modes and violation handling.
204
205
```java { .api }
206
/**
207
* Multi-part compliance modes and validation
208
*/
209
class MultiPartCompliance {
210
/** RFC 7578 compliant parsing */
211
static final MultiPartCompliance RFC7578;
212
213
/** Legacy compatibility mode */
214
static final MultiPartCompliance LEGACY;
215
216
/** Check if violation is allowed */
217
boolean allows(Violation violation);
218
219
/** Get allowed violations */
220
Set<Violation> getAllowed();
221
222
/** Get compliance mode name */
223
String getName();
224
225
/**
226
* Multi-part compliance violations
227
*/
228
enum Violation {
229
/** No boundary specified */
230
NO_BOUNDARY,
231
232
/** Invalid boundary characters */
233
INVALID_BOUNDARY,
234
235
/** Missing final boundary */
236
NO_FINAL_BOUNDARY,
237
238
/** Invalid Content-Disposition header */
239
INVALID_CONTENT_DISPOSITION,
240
241
/** Duplicate part names */
242
DUPLICATE_NAMES;
243
}
244
}
245
```
246
247
### ByteRange Class
248
249
HTTP Range request processing for partial content delivery.
250
251
```java { .api }
252
/**
253
* HTTP Range request processing
254
*/
255
class ByteRange {
256
/** Create byte range */
257
ByteRange(long first, long last);
258
259
/** Get first byte position */
260
long getFirst();
261
262
/** Get last byte position */
263
long getLast();
264
265
/** Get range length */
266
long getLength();
267
268
/** Check if range is suffix range */
269
boolean isSuffix();
270
271
/** Get range as Content-Range header value */
272
String toHeaderValue(long contentLength);
273
274
/** Parse Range header value */
275
static List<ByteRange> parse(String rangeHeader, long contentLength);
276
277
/** Parse single range specification */
278
static ByteRange parseRange(String range, long contentLength);
279
280
/** Check if ranges are satisfiable */
281
static boolean satisfiable(List<ByteRange> ranges, long contentLength);
282
}
283
```
284
285
**Usage Examples:**
286
287
```java
288
import org.eclipse.jetty.http.*;
289
import java.util.concurrent.CompletableFuture;
290
291
// Parse multi-part form data asynchronously
292
MultiPartConfig config = new MultiPartConfig(
293
10 * 1024 * 1024, // maxFileSize: 10MB
294
50 * 1024 * 1024, // maxRequestSize: 50MB
295
8192, // fileSizeThreshold: 8KB
296
"/tmp" // temp directory
297
);
298
299
MultiPartFormData formData = new MultiPartFormData(config);
300
301
// Async parsing
302
Content.Source requestBody = getRequestBodySource();
303
CompletableFuture<MultiPartFormData.Parts> future =
304
formData.parse(requestBody, "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
305
306
future.thenAccept(parts -> {
307
// Process form fields
308
MultiPart.Part textField = parts.get("username");
309
if (textField != null) {
310
String username = readPartContent(textField);
311
}
312
313
// Process file uploads
314
MultiPart.Part fileUpload = parts.get("avatar");
315
if (fileUpload != null && fileUpload.isFile()) {
316
String filename = fileUpload.getFileName();
317
String contentType = fileUpload.getContentType();
318
long size = fileUpload.getLength();
319
320
// Save uploaded file
321
saveFile(fileUpload.getContentSource(), filename);
322
}
323
324
// Release resources
325
parts.release();
326
});
327
328
// Synchronous parsing
329
MultiPartFormData.Parts parts = formData.from(requestBody);
330
331
// Iterate through all parts
332
for (MultiPart.Part part : parts) {
333
String name = part.getName();
334
String filename = part.getFileName();
335
HttpFields headers = part.getHeaders();
336
337
if (part.isFile()) {
338
// Handle file upload
339
processFileUpload(part);
340
} else {
341
// Handle form field
342
String value = readPartContent(part);
343
}
344
}
345
346
// Get multiple parts with same name (e.g., checkboxes)
347
List<MultiPart.Part> selectedItems = parts.getAll("items");
348
for (MultiPart.Part item : selectedItems) {
349
String value = readPartContent(item);
350
}
351
352
// Create multi-part content programmatically
353
MultiPart multiPart = new MultiPart("----WebKitFormBoundary7MA4YWxkTrZu0gW");
354
355
// Add text part
356
MultiPart.Part textPart = createTextPart("username", "john.doe");
357
multiPart.addPart(textPart);
358
359
// Add file part
360
MultiPart.Part filePart = createFilePart("document", "report.pdf",
361
"application/pdf", fileContent);
362
multiPart.addPart(filePart);
363
364
// Multi-part byte ranges for HTTP Range responses
365
MultiPartByteRanges byteRanges = new MultiPartByteRanges("text/plain");
366
367
// Add range parts
368
ByteRange range1 = new ByteRange(0, 499); // bytes 0-499
369
ByteRange range2 = new ByteRange(1000, 1499); // bytes 1000-1499
370
371
byteRanges.addPart(range1, getContentSource(0, 500));
372
byteRanges.addPart(range2, getContentSource(1000, 500));
373
374
String contentType = byteRanges.getContentType();
375
// "multipart/byteranges; boundary=..."
376
377
// Parse HTTP Range header
378
String rangeHeader = "bytes=0-499,1000-1499";
379
long contentLength = 2000;
380
List<ByteRange> ranges = ByteRange.parse(rangeHeader, contentLength);
381
382
for (ByteRange range : ranges) {
383
long first = range.getFirst();
384
long last = range.getLast();
385
long length = range.getLength();
386
387
String contentRange = range.toHeaderValue(contentLength);
388
// "bytes 0-499/2000" or "bytes 1000-1499/2000"
389
}
390
391
// Check if ranges are satisfiable
392
boolean satisfiable = ByteRange.satisfiable(ranges, contentLength);
393
394
// Multi-part compliance handling
395
MultiPartCompliance compliance = MultiPartCompliance.RFC7578;
396
ComplianceViolation.Listener listener =
397
(mode, violation, details) -> {
398
System.err.println("Multi-part violation: " + violation + " - " + details);
399
};
400
401
// Helper methods (implementation dependent)
402
private String readPartContent(MultiPart.Part part) {
403
// Read part content as string
404
return null; // Implementation specific
405
}
406
407
private void saveFile(Content.Source source, String filename) {
408
// Save file to disk
409
}
410
411
private void processFileUpload(MultiPart.Part part) {
412
// Process uploaded file
413
}
414
415
private MultiPart.Part createTextPart(String name, String value) {
416
// Create text form field part
417
return null; // Implementation specific
418
}
419
420
private MultiPart.Part createFilePart(String name, String filename,
421
String contentType, byte[] content) {
422
// Create file upload part
423
return null; // Implementation specific
424
}
425
426
private Content.Source getRequestBodySource() {
427
// Get request body content source
428
return null; // Implementation specific
429
}
430
431
private Content.Source getContentSource(long offset, long length) {
432
// Get content source for byte range
433
return null; // Implementation specific
434
}
435
```