You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a digression from the main ML topic but I was curious about Req and the code you provided for redirections. Since Req is based on Finch, I provided the Finch code as well.
In the code below, the server triggers an HTTP request to an endpoint and streams the response and writes by chunk into a file.
If you want to save the data into a file, you may need to use IO.binwrite to append the chunks into the file process, straight from the Finch doc.
Redirections are allowed and Req follows the redirection. - without doing anything.
I also added a progress count. The progress is saved into the %Req.Response{} under the :private key.
[Front-end note] an HTTP request made from the browser uses the fetch API which can consume a fetch as a stream. To get a download progress, you can use fetch and set a RedableStream on the response body (res.body.getReader()). However, it is easier to use XMLHttpRequest (because you get an accumulator for free with onprogress). For an upload progress, you need to use XMLHttpRequest. This is the reason why Liveview used it in the uploader snippet.
defmoduleFinchStreamdo@doc""" Read or stream download the file server-side """defget_file(url,file_path)docaseFile.ls!()|>Enum.member?(file_path)dotrue->{:ok,file_path}false->download(url,file_path)endend@doc""" Take an URL and save the file to the path. """defdownload(url,file_path)doIO.puts("Starting to process #{inspect(file_path)}...........")# Open a file to which binary chunks will be appended to.# this process is reset in case of redirectionfile_pid=File.open!(file_path,[:write,:binary])unlessis_pid(file_pid),do: raise("File creation problem on disk")# the HTTP stream requestFinch.build(:get,url)|>Finch.stream_while(ExStream.Finch,nil,fn# we put the status in the "acc" to handle redirections{:status,status},_acc->{:cont,status}# - when we receive 302, we put the "location" header in the "acc"# - when we receive a 200, we put the "content-length" and the file name in the "acc",{:headers,headers},acc->handle_headers(headers,acc)# when we receive the "location" tuple, we recurse# otherwise, we write the chunk into the file and print out the current progress.{:data,data},acc->handle_data(data,acc,file_path,file_pid)end)caseFile.close(file_pid)do:ok->{:halt,{file_path,:done}}{:error,_reason}->{:halt,:error}endenddefhandle_headers(headers,status)whenstatusin[301,302,303,307,308]doIO.puts("#{status}: REDIR"){:cont,Enum.find(headers,&(elem(&1,0)=="location"))}enddefhandle_headers(headers,200)do{"content-length",size}=Enum.find(headers,&(elem(&1,0)=="content-length"))casesizedonil->{:cont,{0,nil}}size->{:cont,{0,String.to_integer(size)}}endenddefhandle_headers(_,status)dodbg(status){:halt,:bad_status}enddefhandle_data(_data,{"location",location},file_path,file_pid)doifProcess.alive?(file_pid),do::ok=File.close(file_pid)download(location,file_path)enddefhandle_data(data,{processed,size},file_path,file_pid)docaseIO.binwrite(file_pid,data)do:ok->processed=ifis_integer(size)andsize>0do(processed+byte_size(data))|>tap(fnprocessed->IO.inspect(Float.round(processed*100/size,1),label: "Processed #{inspect(file_path)} %: ")end)elseprocessed+byte_size(data)end{:cont,{processed,size}}{:error,reason}->{:error,reason}endendend
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
This is a digression from the main ML topic but I was curious about
Req
and the code you provided for redirections. SinceReq
is based onFinch
, I provided theFinch
code as well.In the code below, the server triggers an HTTP request to an endpoint and streams the response and writes by chunk into a file.
If you want to save the data into a file, you may need to use
IO.binwrite
to append the chunks into the file process, straight from the Finch doc.Redirections are allowed and
Req
follows the redirection. - without doing anything.I also added a progress count. The progress is saved into the
%Req.Response{}
under the:private
key.The code below is "livebookable" 😁
==================================================================================================
HTTP streaming: server download with progress with Finch and Req
Stream & progress with Finch
We start with the
Finch
library.Test endpoints
We use two endpoints for testing.
We will run concurrently two task using
Task.async_stream
since we use one function with different arguments.Stream & progress with Req
We can download with streams and write the chunks into a file. The code is pretty compact.
We write a module that takes into account the redirections and displays the progress by using the
:private
of the structReq.Response{}
.We run the HTTP calls concurrently with
Task.async
andTask.await_many
.We can alternatively use again
Task.async_stream
.Beta Was this translation helpful? Give feedback.
All reactions