Closed wintermeyer closed 3 weeks ago
Important: We should keep the original image. But create alternative ones which we use for our webpage. Than we can always re-optimize in the future.
Hello @wintermeyer , I have been researching on this the whole evening since I have not tackled such before . I have seen various implimentations online. I know you have experience in web perfomance so you probably encountrred something similar to this . What would be the best way to approach this , I would highly appreciate any help
@MICHAELMUNAVU83 After uploading an image I would fire up n processes which use https://github.com/elixir-mogrify/mogrify or something similar to create images which are smaller. I would store them in an extra resource which is a has_many one to the original one. We could store all in the same resource and add one more attribute. I don't care which way you go.
Just for clarification: We don’t need a fancy multiple images for different systems in one element.
But we know that a 7000x7000 pixel photo is overkill for an avatar. So create 2-3 different sizes and use the one which makes most sense for a specific thing.
@wintermeyer , Hello , I could not manage to install ImageMagick which is used to rescale the images , do you know any out of box solutions we can use?
What OS do you use?
@MICHAELMUNAVU83 What OS do you use on your dev system?
I am on a 2017 version of Mac Airbook
I am on a 2017 version of Mac Airbook
Do you have https://brew.sh installed? With that it shouldn't be a problem to brew install imagemagick
Please try it and give me feedback.
I do ,this is what I had tried previously , let me give it another try.
@MICHAELMUNAVU83 In case you can't get imagemagick working on your dev system just create a bash script with the same name and fake it in development.
#!/bin/bash
# Check if the number of arguments is less than 2
if [ "$#" -lt 2 ]; then
echo "Usage: $0 [options] input_file output_file"
exit 1
fi
# Initialize variables for input and output files
input_file=""
output_file=""
# Parse arguments
while [[ "$#" -gt 0 ]]; do
case "$1" in
-*)
# Ignore options
shift
;;
*)
if [ -z "$input_file" ]; then
input_file="$1"
elif [ -z "$output_file" ]; then
output_file="$1"
else
echo "Unexpected argument: $1"
echo "Usage: $0 [options] input_file output_file"
exit 1
fi
shift
;;
esac
done
# Check if input_file and output_file are set
if [ -z "$input_file" ] || [ -z "$output_file" ]; then
echo "Usage: $0 [options] input_file output_file"
exit 1
fi
# Copy the input file to the output file
cp "$input_file" "$output_file"
echo "Image copied from $input_file to $output_file"
Save it and:
chmod +x imagemagick
./imagemagick -any -options -you -want input.jpg output.jpg
It is a workaround that does the job.
Thanks , I will give this a go and advise.
@wintermeyer , this works in my terminal . When I plug it into a phoenix project where I have
config :mogrify,
convert_command: [
path: "/Users/mac/imagemagick",
args: []
]
config :mogrify,
mogrify_command: [
path: "/Users/mac/imagemagick",
args: []
]
config :mogrify,
identify_command: [
path: "/Users/mac/imagemagick",
args: []
]
in config.exs .
If I run an example as such
Mogrify.open("/Users/mac/Downloads/featured.jpeg") |> Mogrify.format("jpeg") |> Mogrify.save()
I get
** (MatchError) no match of right hand side value: {"Arguments: /Users/mac/Downloads/featured.jpeg -format jpeg /var/folders/86/s83z6g953k9bd1twmyhzq8840000gn/T/747382-featured.jpeg\nUnexpected argument: /var/folders/86/s83z6g953k9bd1twmyhzq8840000gn/T/747382-featured.jpeg\nUsage: /Users/mac/imagemagick [options] input_file output_file\n", 1}
(mogrify 0.9.3) lib/mogrify.ex:43: Mogrify.save/2
iex:5: (file)
Any Idea what I might be missing here , have you tried on your local using the bash script?
@MICHAELMUNAVU83 Please try this script:
#!/bin/bash
# Check if the number of arguments is less than 2
if [ "$#" -lt 2 ]; then
echo "Usage: $0 [options] input_file output_file"
exit 1
fi
# Extract the input file (second to last argument) and output file (last argument)
input_file="${@: -2:1}"
output_file="${@: -1}"
# Copy the input file to the output file
cp "$input_file" "$output_file"
echo "Image copied from $input_file to $output_file"
Hey @wintermeyer , big thanks for this , we are moving . So now the function runs well , if I do
def test do
Mogrify.open("/Users/mac/Documents/featured.jpeg")
|> Mogrify.format("png")
|> Mogrify.save(in_place: true)
end
It gives this
%Mogrify.Image{
path: "/Users/mac/Documents/featured.png",
ext: ".png",
format: "png",
width: nil,
height: nil,
animated: false,
frame_count: 1,
buffer: nil,
operations: [],
dirty: %{}
}
But in the documents folder , there is no such file . If I try to specify a place for saving eg
def test do
Mogrify.open("/Users/mac/Documents/featured.jpeg")
|> Mogrify.format("png")
|> Mogrify.save(path: "/Users/mac/Documents")
end
I get
** (File.CopyError) could not copy from "/var/folders/86/s83z6g953k9bd1twmyhzq8840000gn/T/620794-featured.png" to "/Users/mac/Documents": no such file or directory
(elixir 1.15.7) lib/file.ex:717: File.copy!/3
(mogrify 0.9.3) lib/mogrify.ex:48: Mogrify.save/2
iex:51: (file)
If I do it without specifying a location
def test do
Mogrify.open("/Users/mac/Documents/featured.jpeg")
|> Mogrify.format("png")
|> Mogrify.save()
end
I get
%Mogrify.Image{
path: "/var/folders/86/s83z6g953k9bd1twmyhzq8840000gn/T/271018-featured.png",
ext: ".png",
format: "png",
width: nil,
height: nil,
animated: false,
frame_count: 1,
buffer: nil,
operations: [],
dirty: %{}
}
But This File does not exist
def test do Mogrify.open("/Users/mac/Documents/featured.jpeg") |> Mogrify.format("png") |> Mogrify.save(in_place: true) end
It gives this
%Mogrify.Image{ path: "/Users/mac/Documents/featured.png", ext: ".png", format: "png", width: nil, height: nil, animated: false, frame_count: 1, buffer: nil, operations: [], dirty: %{} }
But in the documents folder , there is no such file .
Did you test the same with the Bash script in the command line?
$ imagemagick /Users/mac/Documents/featured.jpeg /Users/mac/Documents/featured.png
Does that work?
Yes , and it copies
Does this work?
open("/Users/mac/Documents/featured.jpeg") |> resize("100x100") |> save(in_place: true)
Is there any sort of debugging information in the console?
I have
%Mogrify.Image{
path: "/Users/mac/Documents/featured.jpeg",
ext: ".jpeg",
format: nil,
width: nil,
height: nil,
animated: false,
frame_count: 1,
buffer: nil,
operations: [],
dirty: %{}
}
But the image stays the same , not 100 by 100
I have
%Mogrify.Image{ path: "/Users/mac/Documents/featured.jpeg", ext: ".jpeg", format: nil, width: nil, height: nil, animated: false, frame_count: 1, buffer: nil, operations: [], dirty: %{} }
But the image stays the same , not 100 by 100
Of course the image stays the same. You are just using a dummy Bash script which just copies the file from a to b. You will not see any change with that on your dev system. But that is fine because on production with the real imagemagick it will work.
You might want to remove this line from the Bash script:
echo "Image copied from $input_file to $output_file"
But I don't know if that makes any difference.
When we remove the line , it does not work.
@wintermeyer , Kindly Advise if we will have something of the sort for each image upload
def resize(image_path, width, height) do
Mogrify.open(image_path)
|> Mogrify.resize("#{width}x#{height}")
|> Mogrify.save(path: "/uploads/resized_#{width}x#{height}_image_path")
end
We probably have to resize 99% of all uploads.
@MICHAELMUNAVU83 Let's solve this with a work around:
Add three sizes of each image:
Just use sensible values for those sizes. We can adapt/change them later anytime.
@wintermeyer , kindly run this function and tell me what you get as an output.
def check_if_image_magick_is_installed do
try do
{output, 0} = System.cmd("convert", ["--version"])
{:ok, output}
rescue
e in ErlangError ->
if e.reason == :enoent do
{:error, "ImageMagick is not installed or not in the system's PATH"}
else
{:error, "An unknown error occurred: #{inspect(e)}"}
end
end
end
Looking good!
iex(1)> try do
...(1)> {output, 0} = System.cmd("convert", ["--version"])
...(1)> {:ok, output}
...(1)> rescue
...(1)> e in ErlangError ->
...(1)> if e.reason == :enoent do
...(1)> {:error, "ImageMagick is not installed or not in the system's PATH"}
...(1)> else
...(1)> {:error, "An unknown error occurred: #{inspect(e)}"}
...(1)> end
...(1)> end
{:ok,
"Version: ImageMagick 7.1.1-32 Q16-HDRI aarch64 22207 https://imagemagick.org\nCopyright: (C) 1999 ImageMagick Studio LLC\nLicense: https://imagemagick.org/script/license.php\nFeatures: Cipher DPC HDRI Modules OpenMP(5.0) \nDelegates (built-in): bzlib fontconfig freetype gslib heic jng jp2 jpeg jxl lcms lqr ltdl lzma openexr png ps raw tiff webp xml zlib zstd\nCompiler: gcc (4.2)\n"}
And just to be sure I tried it with a non existing program name:
iex(3)> try do
...(3)> {output, 0} = System.cmd("negative_example", ["--version"])
...(3)> {:ok, output}
...(3)> rescue
...(3)> e in ErlangError ->
...(3)> if e.reason == :enoent do
...(3)> {:error, "ImageMagick is not installed or not in the system's PATH"}
...(3)> else
...(3)> {:error, "An unknown error occurred: #{inspect(e)}"}
...(3)> end
...(3)> end
{:error,
"An unknown error occurred: %ErlangError{original: :enoent, reason: nil}"}
Good , Thanks
AFAIK we use the uploaded images right now:
But that is a no no. We can not willy nilly force our users to download > 4MB for a single photo. Ideally we have different resolutions for different things. Like one size for an avatar and one for a normal photo. No image should be bigger than 800Kb.