Hello, I created a post (https://elixirforum.com/t/downloading-with-user-token) before and I need a Feature which helps me to send a file that be chunked.
Imagine I have big video and my user should download it, I need to chunk this file, mean this file should be cut to multi parts (chunk).
if I use |> send_resp(200, file.path) it works sometimes . but I think it isn’t a stream file and I can’t create it many parts.
it should be noted, I wanted to use |> send_chunked(200) in my phoenix but I have error.
at least I need to create a download file that sends the file as section to section to my user
Hello @benwilson512, Thanks,
You are thinking it doesn’t matter I want to create my custom chunk? please see this code, it uses streams, I can’t create my custom chunk yet but it uses it and do more job which I don’t need I mean it downloads file on my server, but we could just show file path instead of downloading .
def download(conn, _params) do
url = "http://localhost:4000/images/logo.png"
%HTTPoison.AsyncResponse{id: id} = HTTPoison.get!(url, %{}, stream_to: self())
conn = conn
|> put_resp_content_type("image/event-stream")
|> put_resp_header("Content-disposition","attachment; filename=\"test.png\"")
|> put_resp_header("X-Accel-Redirect", "/tempfile/download/test.png")
|> put_resp_header("Content-Type", "application/octet-stream")
|> send_chunked(200)
process_httpoison_chunks(conn, id)
end
def process_httpoison_chunks(conn, id) do
receive do
%HTTPoison.AsyncStatus{id: ^id} ->
# TODO handle status
process_httpoison_chunks(conn, id)
%HTTPoison.AsyncHeaders{id: ^id, headers: %{"Connection" => "keep-alive"}} ->
# TODO handle headers
process_httpoison_chunks(conn, id)
%HTTPoison.AsyncChunk{id: ^id, chunk: chunk_data} ->
conn |> chunk(chunk_data)
process_httpoison_chunks(conn, id)
%HTTPoison.AsyncEnd{id: ^id} ->
conn
end
end
Because it might be, that it is not even a Stream (in elixirs understanding of the word). The documentation claims to use the kernels sendfile if available. In theory the most efficient way to get the file from disk to socket, as this is a kernel native operation.
The BEAM process that handles your request, wont get scheduled anymore until the sendfile has been finished.
Well, I don’t know what you mean precisely here or that you know what “chunked response” is in HTTP. Judging what you want to achieve, i.e. send video file to client / player, you most likely want to stream the file.
Sending file to client with send_file will, most likely, meet your requirements. It instructs Cowboy to send the file to client, and it will be done in efficient manner, reading parts of the file from disk and sending it to the client as it receives previous parts. This should be good enough to do basic video player for example.
This may not be good enough if you want to do some smart buffering, i.e. want to load just part of the file to buffer next 15s and then when user plays some more, buffer another 15s. You would have to do some sync with player and control the streaming process yourself, and yes in such case probably relying on send_file will not be enough. But for basic streaming it will be enough and efficient enough.
this code works me and lets me create chunked file with size that I want. I just tested it with png file. I understand phoenix handle my request but it is auto, not with custom chunked file size. now my code has a problem in phoenix controller
this error I just need to fix it if my way is true
[error] #PID<0.3708.0> running KhatoghalamWeb.Endpoint (connection #PID<0.3707.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: GET /download
** (exit) an exception was raised:
** (RuntimeError) expected action/2 to return a Plug.Conn, all plugs must receive a connection (conn) and return a connection, got: :ok
(khatoghalam) lib/khatoghalam_web/controllers/client_home_controller.ex:1: KhatoghalamWeb.ClientHomeController.phoenix_controller_pipeline/2
Yes, I need more than now , for example we want to create a video streaming or file, after each activity I want to send my user a part of chunked files. but now I can just send all the file at first and I don’t want it.
Imagine you are in the game and I want to send u a gift after each win a request.
Simply returning an “old” conn at the end of the function won’t probably work as its “meta data” hasn’t been updated, its best to return the conn as it was returned by Plug.Conn.chunk/2 to be sure all metadata got updated.
I have The network connection was lost. error, now there is a problem more , I should find a way that tells the user client download manager , Wait to finish next request