简体   繁体   中英

file:write_file/2 returns {error, badarg} when given a binary

I'm stuck with the very first programming exercise of Programming Erlang (2nd ed.) by Joe Armstrong. The second chapter introduces a brief program that runs locally both a file server and its client. The example only allows the client to list and get files from the server. The exercise requires you to enhance it by adding a put_file feature.

So I added a case condition to receive messages with the put_file atom and write them with the same filename as the source. Then I added a function to the client to read the contents of a file and send its name and contents to the server.

Unfortunately, each time the message reaches the server, the server tells me that I'm passing a bad argument to the file:write_file/2 function (it returns {error, badarg} ). Assuming that the filename parameter is correct, I've checked the documentation and file:read_file/1 returns a binary, while file:write_file/2 takes an iodata, which, if I interpreted the docs correctly, can either be an iolist or binary.

iodata = iolist() | binary()

I'd say the types are right but I'm pretty sure I'm missing something.

Here's the code.

afile_server.erl

-module(afile_server).
-export([start/1, loop/1]).

start(Dir) -> spawn(afile_server, loop, [Dir]).

loop(Dir) ->
  receive
    {Client, list_dir} ->
      Client ! {self(), file:list_dir(Dir)};
    {Client, {get_file, File}} ->
      Full = full(Dir, File),
      Client ! {self(), file:read_file(Full)};
    {Client, {put_file, File, Binary}} ->
      Full = full(Dir, File),
      Client ! {self(), file:write_file(Full, Binary)}
  end,
  loop(Dir).

full(Dir, File) -> filename:join(Dir, File).

afile_client.erl

-module(afile_client).
-export([ls/1, get_file/2, put_file/2]).

ls(Server) ->
  Server ! {self(), list_dir},
  receive
    {Server, FileList} ->
      FileList
  end.

get_file(Server, File) ->
  Server ! {self(), {get_file, File}},
  receive
    {Server, Content} ->
      Content
  end.

put_file(Server, File) ->
  Binary = file:read_file(File),
  Server ! {self(), {put_file, File, Binary}},
  receive
    {Server, ok} ->
      ls(Server);
    {Server, {error, Reason}} ->
      Reason
  end.

file:read_file/1 returns {ok, Binary} or {error, Reason}. You are passing this as Binary in the put_file message, which is why you get an error when you try to give it to file:write_file/2. Change "Binary = file:read_file(File)" to "{ok, Binary} = file:read_file(File)", and it should work.

The implementations the client was bad, because Binary is like to String in Erlang, the function write_file don't need a file descriptor , It needs a Message, then if we rewrite the client function like to :

put_file(Server, File, Message) ->
  Server ! {self(), {put_file, File, Message}},
  receive
    {Server, ok} ->
      ls(Server);
    {Server, {error, Reason}} ->
      Reason
  end.

It's sufficient, and the call be :

afile_client:put_file(Client,"lola","This message is in file."

I did this exercise in client:

{Client,{put_file,File,Message}} ->
          Full=filename:join(Dir,File),
          Client ! {self(),file:write_file(Full,Message)}

In server:

put_file(Server,File,Message) ->
        Server ! {self(),{put_file,File,Message}},
        receive
                {Server,Content}  ->
                        Content
        end.

Regards.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM