1<erl> 2%% inspired from upload.yaws, slightly simplified. 3 4-record(state, {acc, 5 last 6 }). 7 8out(A) -> 9 %% make sure it is post method 10 'POST' = yaws_api:http_request_method(yaws_api:arg_req(A)), 11 case catch get_props(A) of 12 {ok, AllParts} -> 13 Boundary = "3e9876546ecf", 14 RspCType = "multipart/form-data; boundary=" ++ Boundary, 15 case catch format_output(AllParts, Boundary, []) of 16 {ok, Html} -> 17 [{content,RspCType,Html},break]; 18 Else -> 19 [{status,400},{html,io_lib:format("Error: ~p~n", [Else])}] 20 end; 21 {get_more, Cont, NewState} = GetMore -> 22 GetMore; 23 Else -> 24 [{status,400},{html,io_lib:format("Error: ~p~n", [Else])}] 25 end. 26 27format_output([], Boundary, Acc) -> 28 Part = ["--", Boundary, "--\r\n"], 29 {ok, lists:reverse([iolist_to_binary(Part)|Acc])}; 30format_output([{head, {Name, Hdrs}}|Rest], Boundary, Acc0) -> 31 {_, Name} = lists:keyfind("name", 1, Hdrs), 32 Acc1 = case lists:keyfind("filename", 1, Hdrs) of 33 {_, FName} -> 34 {_, CType} = lists:keyfind(content_type, 1, Hdrs), 35 Part = ["--", Boundary, "\r\n", 36 "Content-Disposition: form-data; name=\"",Name,"\"; filename=\"",FName,"\"\r\n", 37 "Content-Type: ",CType,"\r\n\r\n"], 38 [iolist_to_binary(Part)|Acc0]; 39 false -> 40 Part = ["--", Boundary, "\r\n", 41 "Content-Disposition: form-data; name=\"",Name,"\"", 42 "\r\n\r\n"], 43 [iolist_to_binary(Part)|Acc0] 44 end, 45 format_output(Rest, Boundary, Acc1); 46format_output([{part_body, Body}|Rest], Boundary, Acc) -> 47 format_output(Rest, Boundary, [Body|Acc]); 48format_output([{body, Body}|Rest], Boundary, Acc) -> 49 Part = [Body, "\r\n"], 50 format_output(Rest, Boundary, [iolist_to_binary(Part)|Acc]); 51format_output([H|_], _, _) -> 52 {error, {invalid_multipart_element, H}}. 53 54 55 56 57get_state(A) -> 58 case A#arg.state of 59 undefined -> #state{acc = [], last = false}; 60 State0 -> State0 61 end. 62 63 64get_props(A) -> 65 Parse = yaws_api:parse_multipart_post(A), 66 State = get_state(A), 67 case Parse of 68 {cont, Cont, Res} -> 69 case add_file_chunk(A, Res, State) of 70 {done, Result} -> {ok, Result}; 71 {cont, NewState} -> {get_more, Cont, NewState} 72 end; 73 {result, Res} -> 74 case add_file_chunk(A, Res, State#state{last = true}) of 75 {done, Result} -> {ok, Result}; 76 {cont, _} -> {error, multipart_parsing_error} 77 end; 78 {error, Reason} = Error -> 79 Error 80 end. 81 82 83add_file_chunk(A, [H | Tail], #state{acc = Acc0} = State0) -> 84 Acc1 = [H | Acc0], 85 State1 = State0#state{acc = Acc1}, 86 add_file_chunk(A, Tail, State1); 87add_file_chunk(A, [], #state{last = false} = State) -> 88 {cont, State}; 89add_file_chunk(A, [], #state{last = true, acc = RAcc} = State) -> 90 Data = lists:reverse(RAcc), 91 {done, Data}. 92</erl> 93