From 6f111ffbb0b56f907fee574a54a75ae0e9d11e76 Mon Sep 17 00:00:00 2001 From: Anton Maminov Date: Sun, 23 Dec 2018 13:24:27 +0200 Subject: [PATCH] allow to set filename for send_file if the disposition type matches "attachment", this indicates that the recipient should prompt the user to save the response locally, rather than process it normally --- spec/helpers_spec.cr | 12 ++++++++ src/kemal/helpers/helpers.cr | 57 +++++++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/spec/helpers_spec.cr b/spec/helpers_spec.cr index 21c0cdf..77737bd 100644 --- a/spec/helpers_spec.cr +++ b/spec/helpers_spec.cr @@ -1,4 +1,5 @@ require "./spec_helper" +require "./handler_spec" describe "Macros" do describe "#public_folder" do @@ -114,6 +115,17 @@ describe "Macros" do response.headers["Content-Type"].should eq("application/octet-stream") response.headers["Content-Length"].should eq("6") end + + it "sends file with given path and given filename" do + get "/" do |env| + send_file env, "./spec/asset/hello.ecr", filename: "image.jpg" + end + + request = HTTP::Request.new("GET", "/") + response = call_request_on_app(request) + response.status_code.should eq(200) + response.headers["Content-Disposition"].should eq("attachment; filename=\"image.jpg\"") + end end describe "#gzip" do diff --git a/src/kemal/helpers/helpers.cr b/src/kemal/helpers/helpers.cr index f487c99..b630192 100644 --- a/src/kemal/helpers/helpers.cr +++ b/src/kemal/helpers/helpers.cr @@ -109,7 +109,13 @@ end # ``` # send_file env, "./path/to/file", "image/jpeg" # ``` -def send_file(env : HTTP::Server::Context, path : String, mime_type : String? = nil) +# +# Also you can set the filename and the disposition +# +# ``` +# send_file env, "./path/to/file", filename: "image.jpg", disposition: "attachment" +# ``` +def send_file(env : HTTP::Server::Context, path : String, mime_type : String? = nil, *, filename : String? = nil, disposition : String? = nil) config = Kemal.config.serve_static file_path = File.expand_path(path, Dir.current) mime_type ||= Kemal::Utils.mime_type(file_path) @@ -120,6 +126,7 @@ def send_file(env : HTTP::Server::Context, path : String, mime_type : String? = request_headers = env.request.headers filesize = File.size(file_path) filestat = File.info(file_path) + attachment(env, filename, disposition) Kemal.config.static_headers.try(&.call(env.response, file_path, filestat)) @@ -147,6 +154,31 @@ def send_file(env : HTTP::Server::Context, path : String, mime_type : String? = return end +# Send a file with given data and default `application/octet-stream` mime_type. +# +# ``` +# send_file env, data_slice +# ``` +# +# Optionally you can override the mime_type +# +# ``` +# send_file env, data_slice, "image/jpeg" +# ``` +# +# Also you can set the filename and the disposition +# +# ``` +# send_file env, data_slice, filename: "image.jpg", disposition: "attachment" +# ``` +def send_file(env : HTTP::Server::Context, data : Slice(UInt8), mime_type : String? = nil, *, filename : String? = nil, disposition : String? = nil) + mime_type ||= "application/octet-stream" + env.response.content_type = mime_type + env.response.content_length = data.bytesize + attachment(env, filename, disposition) + env.response.write data +end + private def multipart(file, env : HTTP::Server::Context) # See http://httpwg.org/specs/rfc7233.html fileb = file.size @@ -188,22 +220,13 @@ private def multipart(file, env : HTTP::Server::Context) end end -# Send a file with given data and default `application/octet-stream` mime_type. -# -# ``` -# send_file env, data_slice -# ``` -# -# Optionally you can override the mime_type -# -# ``` -# send_file env, data_slice, "image/jpeg" -# ``` -def send_file(env : HTTP::Server::Context, data : Slice(UInt8), mime_type : String? = nil) - mime_type ||= "application/octet-stream" - env.response.content_type = mime_type - env.response.content_length = data.bytesize - env.response.write data +# Set the Content-Disposition to "attachment" with the specified filename, +# instructing the user agents to prompt to save. +private def attachment(env : HTTP::Server::Context, filename : String? = nil, disposition : String? = nil) + disposition = "attachment" if disposition.nil? && filename + if disposition && filename + env.response.headers["Content-Disposition"] = "#{disposition}; filename=\"#{File.basename(filename)}\"" + end end # Configures an `HTTP::Server::Response` to compress the response