Closed Lazarus404 closed 6 years ago
HI @Lazarus404 ,
the filename information is defined in the headers. The code you sent seems correct to me.
When zipflow starts streaming the headers are already flushed so it is not possible to change that information. Another thing to keep in mind is that zipflow is not aware of the io device as it depends on a function given by the caller to write the contents of the zip archive.
If you provide more information maybe I could help you. Could you send the headers when you hit the url that triggers that code? For instance, in linux using curl
:
$ curl -s -D- -o/dev/null URL
Hi Diego,
I actually worked it out. I was being a muppet of huge proportions and simply reading my old code wrong :) I feel like such a noob. I'd been looking at it for so long, I didn't spot what was staring me in the face.
Btw, in case you want to add a snippet for supporting Phoenix / Plug, you can download files and folders like this:
def download(conn, path, as_download \\ false)
def download(conn, path, _) when is_list(path) do
download_file_list(conn, path)
end
def download(conn, path, as_download) do
if File.dir?(path),
do:
download_dir(conn, path),
else:
download_file(conn, path, as_download)
end
def download_dir(conn, path) do
conn = conn
|> put_resp_header("Content-Disposition", "attachment; filename=\"#{Path.basename(path)}.zip\"")
|> put_resp_content_type(Plug.MIME.type("zip"))
|> send_chunked(200)
chunk_file = fn d ->
chunk(conn, d)
end
basepath = Path.absname(path)
rename = &Path.relative_to(&1, basepath)
Zipflow.Stream.init
|> Zipflow.OS.dir_entry(chunk_file, path, [rename: rename])
|> Zipflow.Stream.flush(chunk_file)
conn
end
def download_file(conn, path, as_download) do
stat = File.stat!(path, time: :posix)
is_mobile = maybe_is_mobile(conn)
conn = if as_download and not is_mobile,
do: conn |> put_resp_header("Content-Disposition", "attachment; filename=\"#{Path.basename(path)}\""),
else: conn
conn = conn
|> put_resp_content_type(:mimerl.filename(path))
|> put_resp_header("Content-Length", "#{stat.size}")
|> put_resp_header("Content-Transfer-Encoding", "binary")
|> put_resp_header("Cache-Control", "must-revalidate, post-check=0, pre-check=0")
|> send_chunked(200)
File.stream!(path, [], @chunk_size)
|> Enum.into(conn)
end
def download_file_list(conn, paths) do
basepath = paths |> List.first() |> Path.dirname() |> Path.absname()
conn = conn
|> put_resp_content_type(Plug.MIME.type("zip"))
|> put_resp_header("Content-Disposition", "attachment; filename=\"#{Path.basename(basepath)}.zip\"")
|> send_chunked(200)
chunk_file = fn d ->
chunk(conn, d)
end
rename = &Path.relative_to(&1, basepath)
context = Zipflow.Stream.init
Enum.reduce(paths, context, fn(path, ctx) ->
path = Path.absname(path)
if File.dir?(path),
do: Zipflow.OS.dir_entry(ctx, chunk_file, path, [rename: rename]),
else: Zipflow.OS.file_entry(ctx, chunk_file, rename.(path), path)
end)
|> Zipflow.Stream.flush(chunk_file)
conn
end
Thanks again, Lee
I've been banging my head against a wall with this:
This is my zip handler for streaming zips of files from paths. This works great, but in Firefox, the downloaded zip has no extension. The file doesn't take its filename from Content Disposition at all. So, this must be set by the ZipFlow module itself, somewhere, but I'm currently too tired and braindead to figure it out :-)
How can I set the downloaded filename, with Zipflow, if the browser is ignoring the Content-Disposition header?