Adjuntos desde binario

This commit is contained in:
2025-08-22 10:21:47 -03:00
parent 28c63c5c60
commit c6b38e2c0c

View File

@ -6,23 +6,62 @@ defmodule ApiWeb.AttachmentController do
def index(conn, params) do def index(conn, params) do
decode = """ decode = """
SELECT convert_from(decrypt(decode('#{params["idstudyattachment"]}', 'hex')::bytea, '1nf0rm3'::bytea, 'aes'), 'UTF8'); SELECT convert_from(
decrypt(decode('#{params["idstudyattachment"]}', 'hex')::bytea, '1nf0rm3'::bytea, 'aes'),
'UTF8'
);
""" """
idstudyattachment = Repo.query!(decode).rows |> hd() |> hd()
idstudyattachment = String.to_integer(idstudyattachment) idstudyattachment =
Repo.query!(decode).rows
|> hd()
|> hd()
|> String.to_integer()
[attachment | _] = get_attachment(idstudyattachment) [attachment | _] = get_attachment(idstudyattachment)
Logger.info("Adjunto: #{inspect(attachment)}") Logger.info("Adjunto: #{inspect(attachment)}")
file_path = attachment[:path] case attachment do
if File.exists?(file_path) do # Caso 1: archivo en filesystem
conn %{path: path} when not is_nil(path) ->
|> put_resp_content_type("application/#{attachment[:format]}") if File.exists?(path) do
|> put_resp_header("content-disposition", "inline; filename=#{attachment[:fullname]}") conn
|> send_file(200, file_path) |> put_resp_content_type("application/#{attachment[:format]}")
else |> put_resp_header("content-disposition", "inline; filename=#{attachment[:fullname]}")
conn |> send_file(200, path)
|> send_resp(404, "File not found") else
conn |> send_resp(404, "File not found")
end
# Caso 2: archivo en S3 (zip → redirige)
%{s3_path: s3_path, format: "zip"} when not is_nil(s3_path) ->
conn |> redirect(external: s3_path)
# Caso 3: archivo en S3 (otros formatos → preview o descarga)
%{s3_path: s3_path} when not is_nil(s3_path) ->
redirect_or_preview(conn, s3_path, attachment[:fullname])
# Caso 4: ni path ni s3_path → usar campo attachment (bytea)
%{path: nil, s3_path: nil, attachment: bin} when not is_nil(bin) ->
content_type = content_type_for(attachment[:format])
disposition =
if content_type == "application/octet-stream" do
"attachment"
else
"inline"
end
conn
|> put_resp_content_type(content_type)
|> put_resp_header(
"content-disposition",
"#{disposition}; filename=#{attachment[:fullname]}"
)
|> send_resp(200, bin)
# Caso default
_ ->
conn |> send_resp(400, "Invalid attachment")
end end
end end
@ -37,6 +76,67 @@ defmodule ApiWeb.AttachmentController do
format: sa.format, format: sa.format,
fullname: fragment("concat(name, '.', format)") fullname: fragment("concat(name, '.', format)")
} }
Repo.all(query) Repo.all(query)
end end
defp redirect_or_preview(conn, path, fullname) do
case HTTPoison.get(path, [], [follow_redirect: false, hackney: [pool: :default]]) do
{:ok, %HTTPoison.Response{status_code: 302, headers: headers}} ->
handle_redirect(conn, headers, fullname)
{:ok, %HTTPoison.Response{status_code: 200, body: body, headers: file_headers}} ->
preview_attachment(conn, body, file_headers, fullname)
{:error, reason} ->
Logger.error("Error al obtener el adjunto: #{inspect(reason)}")
conn |> send_resp(404, "File not found")
end
end
defp handle_redirect(conn, headers, fullname) do
case List.keyfind(headers, "location", 0) do
{"location", redirected_url} ->
Logger.info("Redirigiendo a: #{redirected_url}")
case HTTPoison.get(redirected_url, [], [follow_redirect: true]) do
{:ok, %HTTPoison.Response{status_code: 200, body: body, headers: file_headers}} ->
preview_attachment(conn, body, file_headers, fullname)
{:error, reason} ->
Logger.error("Error al descargar el adjunto tras redirección: #{inspect(reason)}")
conn |> send_resp(404, "File not found")
end
_ ->
Logger.error("Redirección sin ubicación válida.")
conn |> send_resp(404, "File not found")
end
end
defp preview_attachment(conn, body, file_headers, fullname) do
content_type = get_content_type(file_headers)
conn
|> put_resp_content_type(content_type)
|> put_resp_header("content-disposition", "inline; filename=#{fullname}")
|> send_resp(200, body)
end
defp get_content_type(headers) do
case List.keyfind(headers, "content-type", 0) do
{"content-type", content_type} -> content_type
_ -> "application/octet-stream"
end
end
defp content_type_for(format) do
case String.downcase(format || "") do
"pdf" -> "application/pdf"
"jpg" -> "image/jpeg"
"jpeg" -> "image/jpeg"
"png" -> "image/png"
_ -> "application/octet-stream"
end
end
end end