An HTTP/1.1 server for zig
0

Configure Feed

Select the types of activity you want to include in your feed.

http.zig / examples / 10_file_upload.zig
5.1 kB 135 lines
1const std = @import("std"); 2const httpz = @import("httpz"); 3const Allocator = std.mem.Allocator; 4 5const PORT = 8810; 6 7// This example demonstrates handling file uploads using multipart/form-data. 8// It shows how to: 9// 1. Parse multipart form data 10// 2. Access uploaded file content and metadata 11// 3. Save uploaded files to disk 12// 4. Handle both file and regular form fields 13 14pub fn main(init: std.process.Init) !void { 15 const allocator = init.gpa; 16 17 var server = try httpz.Server(void).init(init.io, allocator, .{ 18 .address = .localhost(PORT), 19 .request = .{ 20 // Configure the maximum number of multipart form fields 21 // This must be > 0 to use req.multiFormData() 22 .max_multiform_count = 10, 23 24 // Set a reasonable max body size for file uploads (10MB in this example) 25 .max_body_size = 10 * 1024 * 1024, 26 }, 27 }, {}); 28 defer server.deinit(); 29 defer server.stop(); 30 31 var router = try server.router(.{}); 32 33 router.get("/", index, .{}); 34 router.post("/upload", upload, .{}); 35 36 std.debug.print("listening http://localhost:{d}/\n", .{PORT}); 37 std.debug.print("Open http://localhost:{d}/ in your browser to test file uploads\n", .{PORT}); 38 39 try server.listen(); 40} 41 42fn index(_: *httpz.Request, res: *httpz.Response) !void { 43 res.content_type = .HTML; 44 res.body = 45 \\<!DOCTYPE html> 46 \\<h1>File Upload Example</h1> 47 \\<form method=post action=/upload enctype="multipart/form-data"> 48 \\ <p>Name: <input name=name value=Leto></p> 49 \\ <p>Description: <textarea name=description>A test file upload</textarea></p> 50 \\ <p>File: <input type=file name=file required></p> 51 \\ <p>File 2 (optional): <input type=file name=file2></p> 52 \\ <p><button type=submit>Upload</button></p> 53 \\</form> 54 ; 55} 56 57fn upload(req: *httpz.Request, res: *httpz.Response) !void { 58 // Parse the multipart form data 59 // This returns a MultiFormKeyValue which contains both regular fields and file uploads 60 const form_data = try req.multiFormData(); 61 62 // Access regular form fields 63 const name = form_data.get("name"); 64 const description = form_data.get("description"); 65 66 // Access uploaded files 67 // Each value has: 68 // - .value: []const u8 (the file content) 69 // - .filename: ?[]const u8 (the original filename, if provided) 70 const file = form_data.get("file"); 71 const file2 = form_data.get("file2"); 72 73 // Build the response 74 res.content_type = .HTML; 75 const writer = res.writer(); 76 77 try writer.writeAll("<!DOCTYPE html><h1>Upload Successful!</h1>"); 78 79 // Display regular form fields 80 try writer.writeAll("<h2>Form Fields:</h2><ul>"); 81 if (name) |n| { 82 try writer.print("<li><strong>Name:</strong> {s}</li>", .{n.value}); 83 } 84 if (description) |d| { 85 try writer.print("<li><strong>Description:</strong> {s}</li>", .{d.value}); 86 } 87 try writer.writeAll("</ul>"); 88 89 // Display file information and optionally save to disk 90 if (file) |f| { 91 try writer.writeAll("<h2>File Upload:</h2><ul>"); 92 try writer.print("<li><strong>Filename:</strong> {s}</li>", .{f.filename orelse "no filename provided"}); 93 try writer.print("<li><strong>Size:</strong> {} bytes</li>", .{f.value.len}); 94 try writer.writeAll("</ul>"); 95 96 // // Example: Save the file to disk 97 // if (f.filename) |filename| { 98 // const safe_filename = try std.fmt.allocPrint(res.arena, "uploaded_{s}", .{filename}); 99 // const file_handle = try std.fs.cwd().createFile(safe_filename, .{}); 100 // defer file_handle.close(); 101 102 // try file_handle.writeAll(f.value); 103 // try writer.print("<p>✓ File saved as: <code>{s}</code></p>", .{safe_filename}); 104 // } 105 106 try writer.writeAll("<h3>Content Preview:</h3><pre>"); 107 try writer.print("\\x{x}", .{f.value[0..@min(200, f.value.len)]}); 108 if (f.value.len > 200) { 109 try writer.writeAll("..."); 110 } 111 try writer.writeAll("</pre>"); 112 } 113 114 // Handle optional second file 115 if (file2) |f| { 116 try writer.writeAll("<h2>Second File Upload:</h2><ul>"); 117 try writer.print("<li><strong>Filename:</strong> {s}</li>", .{f.filename orelse "no filename provided"}); 118 try writer.print("<li><strong>Size:</strong> {} bytes</li>", .{f.value.len}); 119 try writer.writeAll("</ul>"); 120 } 121 122 // Iterate over all form fields (useful for debugging or when field names are dynamic) 123 try writer.writeAll("<h2>All Fields (Debug):</h2><ul>"); 124 var it = form_data.iterator(); 125 while (it.next()) |kv| { 126 if (kv.value.filename) |filename| { 127 try writer.print("<li><strong>{s}</strong> (file): {s} ({} bytes)</li>", .{ kv.key, filename, kv.value.value.len }); 128 } else { 129 try writer.print("<li><strong>{s}</strong>: {s}</li>", .{ kv.key, kv.value.value }); 130 } 131 } 132 try writer.writeAll("</ul>"); 133 134 try writer.writeAll("<p><a href=\"/\">Upload another file</a></p>"); 135}