java 64 lines · 8 steps

Parsing multipart form data by hand in Java

A byte-level parser that splits a multipart/form-data body on its boundary and extracts each uploaded file.

Explained by highlit
1public final class MultipartParser {
2 
3 public record FilePart(String name, String filename, String contentType, byte[] content) {}
4 
5 private final byte[] data;
6 private final byte[] boundary;
7 
8 public MultipartParser(byte[] data, String contentType) {
9 this.data = data;
10 String marker = "boundary=";
11 int i = contentType.indexOf(marker);
12 if (i < 0) throw new IllegalArgumentException("missing boundary");
13 String b = contentType.substring(i + marker.length()).trim();
14 if (b.startsWith("\"")) b = b.substring(1, b.length() - 1);
15 this.boundary = ("--" + b).getBytes(StandardCharsets.ISO_8859_1);
16 }
17 
18 public List<FilePart> parse() {
19 List<FilePart> parts = new ArrayList<>();
20 int pos = indexOf(boundary, 0);
21 while (pos >= 0) {
22 int start = pos + boundary.length;
23 if (start + 2 <= data.length && data[start] == '-' && data[start + 1] == '-') break;
24 start += 2;
25 int headerEnd = indexOf("\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1), start);
26 if (headerEnd < 0) break;
27 String headers = new String(data, start, headerEnd - start, StandardCharsets.ISO_8859_1);
28 int bodyStart = headerEnd + 4;
29 int next = indexOf(boundary, bodyStart);
30 int bodyEnd = next - 2;
31 FilePart part = buildPart(headers, Arrays.copyOfRange(data, bodyStart, bodyEnd));
32 if (part != null) parts.add(part);
33 pos = next;
34 }
35 return parts;
36 }
37 
38 private FilePart buildPart(String headers, byte[] body) {
39 String name = extract(headers, "name=\"", "\"");
40 String filename = extract(headers, "filename=\"", "\"");
41 if (filename == null) return null;
42 String type = extract(headers, "Content-Type: ", "\r\n");
43 return new FilePart(name, filename, type != null ? type.trim() : "application/octet-stream", body);
44 }
45 
46 private String extract(String source, String prefix, String suffix) {
47 int i = source.indexOf(prefix);
48 if (i < 0) return null;
49 i += prefix.length();
50 int j = source.indexOf(suffix, i);
51 return j < 0 ? source.substring(i) : source.substring(i, j);
52 }
53 
54 private int indexOf(byte[] needle, int from) {
55 outer:
56 for (int i = from; i <= data.length - needle.length; i++) {
57 for (int j = 0; j < needle.length; j++) {
58 if (data[i + j] != needle[j]) continue outer;
59 }
60 return i;
61 }
62 return -1;
63 }
64}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Multipart bodies must be scanned as raw bytes, not decoded strings, so file content survives intact.
  2. 2The boundary from the Content-Type header is the delimiter that separates every part in the body.
  3. 3Splitting a format into locate-header, extract-metadata, and copy-body phases keeps a hand-rolled parser manageable.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Parsing multipart form data by hand in Java — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code