Nitrogen框架:能否将上传文件直接存入数据库(以RIAK-KV为例)
Great question! Yes, you absolutely can skip Nitrogen's default ./scratch/ directory and upload files straight to Riak-KV without writing to local disk first. The key is to create a custom upload handler that captures the file data in memory and sends it directly to Riak during the upload process. Here's how to do it:
1. Build a Custom Upload Handler
Create an Erlang module (we'll call it my_riak_upload_handler.erl) that implements Nitrogen's nitro_upload_handler behavior. This handler will manage the upload stream, accumulate file data, and push it to Riak once the upload finishes.
-module(my_riak_upload_handler). -behaviour(nitro_upload_handler). -export([init/2, handle_upload/2, finish_upload/1]). init(_UploadDetails, _Context) -> % Initialize state: start a Riak connection (adjust host/port to your setup) {ok, RiakPid} = riakc_pb:start_link("127.0.0.1", 8087), {ok, #{riak_pid => RiakPid, accumulated_data => <<>>}}. handle_upload({file, FileName, _ContentType, DataChunk}, State) -> % Accumulate the incoming file chunks in state CurrentData = maps:get(accumulated_data, State), UpdatedData = <<CurrentData/binary, DataChunk/binary>>, % Store filename and updated data in state NewState = State#{accumulated_data => UpdatedData, filename => FileName}, {ok, NewState}. finish_upload(State) -> #{ riak_pid := RiakPid, accumulated_data := FullFileBinary, filename := FileName } = State, % Prepare the Riak object: use a bucket of your choice, filename as key (or generate a unique one) RiakBucket = <<"user_uploads">>, RiakKey = list_to_binary(FileName), % Create the object with the full file binary and its content type RiakObject = riakc_obj:new(RiakBucket, RiakKey, FullFileBinary, "application/octet-stream"), % Save the object to Riak Result = case riakc_pb:put(RiakPid, RiakObject) of ok -> {success, #{riak_key => RiakKey}}; {error, Reason} -> {failure, #{reason => Reason}} end, % Clean up the Riak connection (or reuse it with a pool for better performance) riakc_pb:stop(RiakPid), Result.
2. Configure Your Upload Element to Use the Handler
In your Nitrogen page code, specify your custom handler when rendering the #upload{} component:
render() -> #panel{children=[ #upload{ id = file_uploader, handler = my_riak_upload_handler, postback = upload_finished }, #span{id = upload_status, text = "Ready to upload"} ]}.
3. Handle the Upload Completion Postback
Add an event handler in your page module to respond when the upload finishes, and update the UI with the result:
event(upload_finished) -> case wf:upload_result(file_uploader) of {success, #{riak_key := Key}} -> SuccessMsg = io_lib:format("Upload successful! Riak key: ~s", [binary_to_list(Key)]), wf:update(upload_status, #span{text = SuccessMsg, class = "success"}); {failure, #{reason := Reason}} -> ErrorMsg = io_lib:format("Upload failed: ~p", [Reason]), wf:update(upload_status, #span{text = ErrorMsg, class = "error"}) end.
Key Considerations for Production
- Large Files: For big files, accumulating the entire binary in memory isn't ideal. Instead, use Riak's multipart upload API (via
riakc_pb:put_chunk/4) to send chunks as they're received inhandle_upload/2. - Connection Pools: Instead of opening/closing a Riak connection per upload, use a connection pool (like
poolboy) to reuse connections and improve performance. - Metadata: To store extra info (like upload timestamp, user ID), add it to the Riak object's metadata using
riakc_obj:update_metadata/2. - Error Handling: Expand the handler with retries for Riak connection failures, and validate file sizes/types before uploading.
内容的提问来源于stack exchange,提问作者Vianney Sserwanga

