|
@@ -0,0 +1,129 @@
|
|
|
|
+-module(n4u_file).
|
|
|
|
+
|
|
|
|
+-include_lib("n4u/include/n4u.hrl").
|
|
|
|
+-include_lib("kernel/include/file.hrl").
|
|
|
|
+
|
|
|
|
+-export([filename/1, info/3, proc/2]).
|
|
|
|
+
|
|
|
|
+-define(ROOT, application:get_env(n4u, upload, code:priv_dir(n4u))).
|
|
|
|
+-define(NEXT, 256 * 1024). % 256K chunks for best 25MB/s speed
|
|
|
|
+-define(STOP, 0).
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+% callback
|
|
|
|
+
|
|
|
|
+filename(#ftp{sid = _Sid, filename = File_Name}) ->
|
|
|
|
+ File_Name. % filename:join(lists:concat([Sid]), File_Name). % filename:join(nitro:to_list(Sid), File_Name). % todo check
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+% N4U protocols
|
|
|
|
+
|
|
|
|
+info(#ftp{status = {event, _}} = FTP, Req, State) ->
|
|
|
|
+ wf:info(?MODULE, "Event Message: ~p", [FTP#ftp{data = <<>>}]),
|
|
|
|
+ Module = State#cx.module,
|
|
|
|
+ Reply = try Module:event(FTP)
|
|
|
|
+ catch E:R:Stk ->
|
|
|
|
+ wf:error(?MODULE, "Catch: ~p:~p~n~p", [E, R, Stk]),
|
|
|
|
+ [E, R, Stk]
|
|
|
|
+ end,
|
|
|
|
+ {reply, wf:format({io, n4u_nitrogen:render_actions(erlang:get(actions)), Reply}), Req, State};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+info(#ftp{id = Link, sid = _Sid, filename = _FileName, status = <<"init">>,
|
|
|
|
+ block = Block, offset = Offset, 'size' = _TotalSize} = FTP, Req, State) ->
|
|
|
|
+ Root = ?ROOT,
|
|
|
|
+ RelPath = (application:get_env(n4u, filename, n4u_file)):filename(FTP),
|
|
|
|
+ FilePath = filename:join(Root, RelPath),
|
|
|
|
+ ok = filelib:ensure_dir(FilePath),
|
|
|
|
+
|
|
|
|
+ FileSize = case file:read_file_info(FilePath) of
|
|
|
|
+ {ok, Fi} -> Fi#file_info.'size'; %'
|
|
|
|
+ {error, _} -> 0
|
|
|
|
+ end,
|
|
|
|
+
|
|
|
|
+ wf:info(?MODULE, "Info Init: ~p Offset: ~p Block: ~p~n", [FilePath, FileSize, Block]),
|
|
|
|
+
|
|
|
|
+ %Name = {Sid, filename:basename(FileName)},
|
|
|
|
+ Block2 = case Block of
|
|
|
|
+ 0 -> ?STOP;
|
|
|
|
+ _ -> ?NEXT
|
|
|
|
+ end,
|
|
|
|
+ Offset2 = case FileSize >= Offset of
|
|
|
|
+ true -> FileSize;
|
|
|
|
+ false -> 0
|
|
|
|
+ end,
|
|
|
|
+ FTP2 = FTP#ftp{block = Block2, offset = Offset2, filename = RelPath, data = <<>>},
|
|
|
|
+
|
|
|
|
+ catch n4u_async:stop(file, Link),
|
|
|
|
+ n4u_async:start(#handler{module = ?MODULE, class = file, group = n4u, state = FTP2, name = Link}),
|
|
|
|
+
|
|
|
|
+ {reply, wf:format(FTP2), Req, State};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+info(#ftp{id = Link, sid = _Sid, filename = _FileName, status = <<"send">>} = FTP, Req, State) ->
|
|
|
|
+ wf:info(?MODULE, "Info Send: ~p", [FTP#ftp{data = <<>>}]),
|
|
|
|
+
|
|
|
|
+ %Reply = try n4u_async:send(n4u_async:pid({file, Link}), FTP)
|
|
|
|
+ Reply = try n4u_async:send(file, Link, FTP)
|
|
|
|
+ catch E:R:Stk ->
|
|
|
|
+ wf:error(?MODULE, "Info Error call the async: ~p~n~p:~p~n ~p~n", [FTP#ftp{data = <<>>}, E, R, Stk]),
|
|
|
|
+ FTP#ftp{data = <<>>, sid = <<>>, block = ?STOP}
|
|
|
|
+ end,
|
|
|
|
+
|
|
|
|
+ wf:info(?MODULE, "reply ~p", [Reply#ftp{data = <<>>}]),
|
|
|
|
+ {reply, wf:format(Reply), Req, State};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+info(#ftp{status = <<"recv">>} = FTP, Req, State) ->
|
|
|
|
+ {reply, wf:format(FTP), Req, State};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+info(#ftp{status = <<"relay">>} = FTP, Req, State) ->
|
|
|
|
+ {reply, wf:format(FTP), Req, State};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+info(Message, Req, State) ->
|
|
|
|
+ wf:info(?MODULE, "Info Unknown message: ~p", [Message]),
|
|
|
|
+ {unknown, Message, Req, State}.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+% N4U handlers
|
|
|
|
+
|
|
|
|
+proc(init, #handler{state = #ftp{sid = Sid} = FTP} = Async) ->
|
|
|
|
+ wf:info(?MODULE, "Proc Init: ~p", [FTP#ftp{data = <<>>}]),
|
|
|
|
+ wf:send(Sid, FTP#ftp{data = <<>>, status = {event, init}}),
|
|
|
|
+ {ok, Async};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+proc(#ftp{id = Link, sid = Sid, data = Data, filename = _FileName, status = <<"send">>, block = Block} = FTP,
|
|
|
|
+ #handler{state = #ftp{data = _State, 'size' = TotalSize, offset = Offset, filename = RelPath}} = Async) when Offset + Block >= TotalSize ->
|
|
|
|
+ wf:info(?MODULE, "Proc Stop ~p, last piece size: ~p", [FTP#ftp{data = <<>>}, erlang:byte_size(Data)]),
|
|
|
|
+ case file:write_file(filename:join(?ROOT, RelPath), <<Data/binary>>, [append, raw]) of
|
|
|
|
+ {error, Reason} ->
|
|
|
|
+ wf:error(?MODULE, "WRITE FILE ERROR: ~p~n~p", [filename:join(?ROOT, RelPath), Reason]),
|
|
|
|
+ {reply, {error, Reason}, Async};
|
|
|
|
+ ok ->
|
|
|
|
+ wf:info(?MODULE, "WRITE FILE OK: ~p~n", [filename:join(?ROOT, RelPath)]),
|
|
|
|
+ FTP2 = FTP#ftp{data = <<>>, block = ?STOP},
|
|
|
|
+ wf:send(Sid, FTP2#ftp{status = {event, stop}, filename = RelPath}),
|
|
|
|
+ erlang:spawn(
|
|
|
|
+ fun() ->
|
|
|
|
+ n4u_async:stop(file, Link)
|
|
|
|
+ end),
|
|
|
|
+ {stop, normal, FTP2, Async#handler{state = FTP2}}
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+proc(#ftp{sid = _Sid, data = Data, block = Block} = FTP,
|
|
|
|
+ #handler{state = #ftp{data = _State, offset = Offset, filename = RelPath}} = Async) ->
|
|
|
|
+ FTP2 = FTP#ftp{status = <<"send">>, offset = Offset + Block},
|
|
|
|
+ wf:info(?MODULE, "Proc Process ~p", [FTP2#ftp{data = <<>>}]),
|
|
|
|
+ case file:write_file(filename:join(?ROOT, RelPath), <<Data/binary>>, [append, raw]) of
|
|
|
|
+ {error, Reason} ->
|
|
|
|
+ wf:error(?MODULE, "WRITE FILE ERROR 0: ~p~n~p", [filename:join(?ROOT, RelPath), Reason]),
|
|
|
|
+ {reply, {error, Reason}, Async};
|
|
|
|
+ ok ->
|
|
|
|
+ {reply, FTP2#ftp{data = <<>>}, Async#handler{state = FTP2#ftp{filename = RelPath}}}
|
|
|
|
+ end.
|
|
|
|
+
|