ekfa/src/efka_util.erl
2026-04-19 18:23:03 +08:00

132 lines
4.2 KiB
Erlang
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

%%%-------------------------------------------------------------------
%%% @author anlicheng
%%% @copyright (C) 2025, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 19. 4月 2025 16:33
%%%-------------------------------------------------------------------
-module(efka_util).
-author("anlicheng").
%% API
-export([get_file_md5/1]).
-export([timestamp/0, number_format/2, timestamp_ms/0, float_to_binary/2, int_format/2]).
-export([chunks/2, rand_bytes/1, uuid/0, md5/1, sha_uuid/0]).
-export([json_data/1, json_error/2]).
-export([starts_with/2, file_md5/1]).
get_file_md5(FilePath) when is_list(FilePath) ->
{ok, FileData} = file:read_file(FilePath),
Md5Binary = crypto:hash(md5, FileData),
string:lowercase(lists:flatten([io_lib:format("~2.16.0b", [X]) || X <- binary_to_list(Md5Binary)])).
%% 时间,精确到毫秒
timestamp_ms() ->
{Mega, Seconds, Micro} = os:timestamp(),
(Mega * 1000000 + Seconds) * 1000 + Micro div 1000.
timestamp() ->
{Mega, Seconds, _Micro} = os:timestamp(),
Mega * 1000000 + Seconds.
number_format(Num, _Decimals) when is_integer(Num) ->
Num;
number_format(Float, Decimals) when is_float(Float) ->
list_to_float(float_to_list(Float, [{decimals, Decimals}, compact])).
int_format(Num, Len) when is_integer(Num), Len > 0 ->
S = integer_to_list(Num),
case length(S) > Len of
true ->
list_to_integer(lists:sublist(S, 1, Len));
false ->
Num
end.
%% 将数据分组
-spec chunks(list(), integer()) -> [list()].
chunks(List, Size) when is_list(List), is_integer(Size), Size > 0, length(List) =< Size ->
[List];
chunks(List, Size) when is_list(List), is_integer(Size), Size > 0 ->
chunks0(List, Size, Size, [], []).
chunks0([], _, _, [], AccTarget) ->
lists:reverse(AccTarget);
chunks0([], _, _, Target, AccTarget) ->
lists:reverse([lists:reverse(Target) | AccTarget]);
chunks0(List, Size, 0, Target, AccTarget) ->
chunks0(List, Size, Size, [], [lists:reverse(Target) | AccTarget]);
chunks0([Hd | Tail], Size, Num, Target, AccTarget) ->
chunks0(Tail, Size, Num - 1, [Hd | Target], AccTarget).
json_data(Data) ->
jiffy:encode(#{
<<"result">> => Data
}, [force_utf8]).
json_error(ErrCode, ErrMessage) when is_integer(ErrCode), is_binary(ErrMessage) ->
jiffy:encode(#{
<<"error">> => #{
<<"code">> => ErrCode,
<<"message">> => ErrMessage
}
}, [force_utf8]).
uuid() ->
rand_bytes(16).
-spec rand_bytes(Size :: integer()) -> string().
rand_bytes(Size) when is_integer(Size), Size > 0 ->
Size1 = erlang:ceil(Size / 2),
Bytes = crypto:strong_rand_bytes(Size1),
S = lists:flatten([integer_to_list(E, 16) || <<E:4>> <= Bytes]),
lists:sublist(string:to_lower(S), 1, Size).
-spec md5(Str :: binary()) -> binary().
md5(Str) when is_binary(Str) ->
list_to_binary(lists:flatten([hex(X) || <<X:4>> <= erlang:md5(Str)])).
hex(N) when N < 10 ->
$0 + N;
hex(N) ->
$a + (N - 10).
-spec float_to_binary(Num :: number(), integer()) -> binary().
float_to_binary(V, _) when is_integer(V) ->
integer_to_binary(V);
float_to_binary(V, Decimals) when is_float(V), is_integer(Decimals) ->
S = float_to_list(V, [{decimals, Decimals}, compact]),
list_to_binary(S).
%% 生产唯一的uuid
-spec sha_uuid() -> binary().
sha_uuid() ->
Salt = crypto:strong_rand_bytes(32),
Str = string:lowercase(binary:encode_hex(crypto:hash(sha256, Salt))),
binary:part(Str, 1, 32).
-spec starts_with(Binary :: binary(), Prefix :: binary()) -> boolean().
starts_with(Binary, Prefix) when is_binary(Binary), is_binary(Prefix) ->
PrefixSize = byte_size(Prefix),
case Binary of
<<Prefix:PrefixSize/binary, _Rest/binary>> -> true;
_ -> false
end.
-spec file_md5(FilePath :: string()) -> Md5 :: string().
file_md5(FilePath) when is_list(FilePath) ->
{ok, F} = file:open(FilePath, [read, binary]),
Digest = md5_loop(F, crypto:hash_init(md5)),
file:close(F),
lists:flatten(io_lib:format("~32.16.0b", [binary:decode_unsigned(Digest)])).
md5_loop(F, Context) ->
%% 每次读取 1MB可调整块大小
case file:read(F, 1024 * 1024) of
eof ->
crypto:hash_final(Context);
{ok, Bin} ->
md5_loop(F, crypto:hash_update(Context, Bin))
end.