File.write question

Hi everyone.

I am trying to download a file from gmail thru their API with the following code:

case GmailService.message_attachment(token, user.email, attachment.messageID, attachment.attachmentID, []) do
{:ok, file} -> File.write!("./attachments/#{attachment.filename}", file[“data”], [:write, { :enconding, :utf8 }])
{:error, reason} -> IO.puts(reason)
end

The attachment properly downloads and binds the data to “file” constant but I am unable to save the file correctly.

With the File.write command it creates the file to the specified directory, name and size but when I try to open it, it fails as if the file was corrupted.

I have the same scenario with Ruby on Rails and I use a File.Open with the encondig of the file and works fine.

Can anyone guide me on this issue? Do I need to use IO.binwrite?

Thanks for the help.

Joaquin Alcerro

1 Like

You are explicitly using the encoding: :utf8 mode.
Does that mean your input data is actually encoded as UTF8? If not, try using :binary mode instead.

You only need IO.binwrite/2 or IO.write/2 if you open the file via File.open/2 or File.open/3. Check out the documentation for File.open/3 on how to do that.

1 Like

@wmnnd,

Thanks for your feedback. So I tried the following:

  1. First inspected the data
  2. Save the file with different options

The files saves without errors but I keep getting:

Thanks again for your comments and support.

Best Regards,

1 Like

It seems like the data might be Base64-encoded. In that case you’ll have to decode it with Base.decode64/2 before you write it to the file.

2 Likes

If it is a PDF, the first four bytes must match <<0x25, 0x50, 0x44, 0x46,>> ("%PDF"), if they don’t, your incomming stream is probably encoded, please take a look into your full response struct, if you can pull some header information out from it which tells you more about the encoding.

Using some online tools, it seems in fact that your first couple of bytes (taken from raw) match the base64-encoded version of "%PDF", which is JVBERg==, so you need to base64 decode your data before writing it to disc.

1 Like

Thanks for your feedback,

I tried to decode64 but getting a non-alphabet digit found: “-” (byte 45) error.

Not sure what is happening.

Thanks for the help.

1 Like

A google search for “gmail download attachment base64 dash” turned up this SO post and I quote the answer:

I just found out the gmail api return the attachment content in base64url format where the ‘+’ and ‘/’ characters of standard Base64 are respectively replaced by ‘-’ and ‘_’.

So fix the data, decode, write to file.

1 Like

There is actually a variant of Base 64 that uses those characters in order to make them »URL and Filename safe«. Elixir supports it through Base.url_decode64!/2.

4 Likes

Ok this is how I finally get it working thanks to you all.

Use case

  • I am downloading different type of files from Gmail thru Google’s API v1.
  • I am using Elixir 1.4 with HTTPoison
  • I use HTTPoison to make direct calls to API

Situation

  • When I try to open the downloaded file it appears to be corrupted.
  • Apparently I was not decoding the file properly before saving it to disk.

You guys gave me important feedback

  • File was Base64-encoded
  • Gmail encodes the files in a specific Base64 variant where ‘+’ was replaced by ‘-’ and ‘/’ for ‘_’
  • Elixir supports the specific Base64 through Base.url_decode64!/2

This is the code

case GmailService.message_attachment(token, user.email, attachment.messageID, attachment.attachmentID, []) do
      {:ok, file} -> decoded_attachment = Base.url_decode64!(file["data"]) 
                     File.write!("./attachments/#{attachment.filename}", decoded_attachment)
      {:error, reason} -> IO.puts(reason) 
end

Hope this help other with the type of issue.

Thanks to you all.

Best regards.

4 Likes