Hello everyone.
I’m working on an application that lists all your Google Drive files and allows you to upload them to our app (which uses S3).
Unfortunately, it’s not possible to use a file’s URL given by Drive to upload the file using waffle_ecto
with opt allow_urls: true
. The only way is to download the file first to a temporary folder and then uploading it to S3; which it worked, but is it possible to avoid using a temporary folder and upload the file at the same time that is being downloaded?
Well, I’ve tried to do it but I failed. The async_download/2
function was extracted from [{poeticoding}] (Download Large Files with HTTPoison Async Requests) by the author @alvises and it works perfect. I believe the problem is in the chunk
param when I call ExAws.S3.put_object/3
inside the upload_chunk_to_s3/2
function. Currently, the file type of chunk
is an iodata
. I’ve also tried to transform it to a binary
using IO.iodata_to_binary/2
and encoding to base64 with Base.encode64/2
but in both cases I failed.
Here is the code involved:
defmodule GoogleDriveApi do
@moduledoc """
Google Drive API module.
"""
@base_url "https://www.googleapis.com/drive/v3"
@doc """
Downloads a single file from Google Drive API.
"""
def download_file(access_token, file, filename) do
url = @base_url <> "/files/" <> file["id"]
headers = [
Authorization: "Bearer #{access_token}",
Accept: "Application/json; Charset=utf-8"
]
options = [
params: [
alt: "media"
],
stream_to: self(),
async: :once
]
with {:ok, resp} <- HTTPoison.get(url, headers, options),
:ok <- async_download(resp, filename) do
{:ok, ""}
end
end
defp async_download(resp, filename) do
resp_id = resp.id
receive do
%HTTPoison.AsyncStatus{code: 200, id: ^resp_id} ->
HTTPoison.stream_next(resp)
async_download(resp, filename)
%HTTPoison.AsyncStatus{code: status_code, id: ^resp_id} ->
IO.inspect(status_code)
%HTTPoison.AsyncHeaders{headers: _headers, id: ^resp_id} ->
HTTPoison.stream_next(resp)
async_download(resp, filename)
%HTTPoison.AsyncChunk{chunk: chunk, id: ^resp_id} ->
upload_chunk_to_s3(filename, chunk)
HTTPoison.stream_next(resp)
async_download(resp, filename)
%HTTPoison.AsyncEnd{id: ^resp_id} ->
:ok
end
end
defp upload_chunk_to_s3(filename, chunk) do
IO.puts "UPLOAD CHUNK TO S3"
path_to_s3 = "tmp/" <> filename
get_s3_bucket()
|> ExAws.S3.put_object(path_to_s3, chunk)
|> ExAws.request!
end
end
One thing I forgot to mention was I saw some issues on Stackoverflow that could solve this by using Javascript and Buffers. Maybe it’s not possible from the Server side and I have to try it from the Client side?
Any advice would be appreciated!
Cheers,
Agustín Silva.