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