I want to share my experience using the Sharp library from the Node Package Manager (NPM) to dealing with images with heic extension. First, let's talk about the problems.
Background
There are many problems. First is why? Why did I have to mind the .heic
extension?
That problem comes from iPhone
users, they produce this .heic
extension as the output of the image when they use their camera. This behaviour is different from an Android phone, which usually outputs .jpeg
or .jpg
.
This really makes things complicated on the backend
side because browsers
don't usually support displaying .heic
extension out-of-the-box. As a backend engineer, I'm the one who takes responsibility for converting the image that isn't supported by the browser to one that is widely compatible, in addition to that I have to resize the image so it loads faster when displayed on the browser. Also, most of my customers are using iPhones, which is really have to consider be an issue.
So, I had to either convert it to .jpeg
or .webp
. That time, I had an issue with the .webp
result, for example, the image is being rotated by 90 degrees for some reason, which I think could be easily solved by calling the rotate() method on the Sharp library. At the end, I decided to stick with .jpeg
while keeping the EXIF metadata to keep the rotation and other metadata.
The other problem is Sharp library doesn't come out-of-the-box with support for converting the .heic
extension. So, what should I do in this scenario?
Solution
After a quick reading of the sharp's
documentation, I decided to stick with the sharp
library, I was excited to find out that it may have the ability to process files with the heic
extension. It was possible because Sharp actually uses a library called libvips
as the underlying backend for processing the image.
Libvips has heic support, but why doesn't my Sharp library support it?
We already know that sharp
is using libvips
. When we install the sharp
library with npm install sharp
, it'll also download the libvips binary inside the node_modules
directory and sharp
will use that binary, but it has limited support for image extensions. What we can do to make it wider support is to do a custom, manual installation of libvips, I'm talking about building libvips
from source.
Make it work!
It takes a lot of steps to get there. Here is it:
- Install build tools.
- Install
ninja
&meson
(build from source) - Install
libvips
and it's dependencies - Test
libvips
installation - Install
sharp
and its dependencies
Let's get started (I assume you use the Debian Linux distribution).
I highly recommend you use a Docker container with a Debian image; I will provide a full Dockerfile
below.
1. Install build tools
Here's where we're going to install git, Python, and other essential build tools.
# Install dependencies
sudo apt-get update && \
sudo apt-get install -y curl python3 git build-essential pkg-config
2. Install ninja & meson (build from source)
Let's install ninja
first
# Install ninja
git clone --branch=v1.12.1 --depth=1 https://github.com/ninja-build/ninja.git && \
cd ninja && \
./configure.py --bootstrap && \
chmod +x ninja && \
sudo mv ninja /usr/local/bin/ninja
Install meson
# Install meson
git clone --branch=1.6.1 --depth=1 https://github.com/mesonbuild/meson.git && \
cd meson && \
./packaging/create_zipapp.py --outfile meson.pyz --interpreter '/usr/bin/env python3' && \
chmod +x meson.pyz && \
sudo mv meson.pyz /usr/local/bin/meson
Done, on to step 3
3. Install libvips and its dependencies
# install runtime dependencies
sudo apt-get update && \
sudo apt-get install -y \
curl python3 build-essential git pkg-config libglib2.0-dev libexpat1-dev libheif-dev\
liblcms2-dev libjpeg-dev libpng-dev libwebp-dev libexif-dev
# Build libvips
git clone --branch=v8.16.0 --depth=1 https://github.com/libvips/libvips.git && \
cd libvips && \
meson setup build --prefix /usr/local && \
cd build && \
meson compile && \
meson test && \
meson install && \
sudo ldconfig && \
cd .. && \
rm -rf libvips
3. Test libvips installation
At this stage, you have libvips
installed on your machine, you can test if it's properly installed.
vips --version
4. Install sharp
and its dependencies
Install node-addon-api
npm install node-addon-api@^8.1.0
Install node-gyp
npm install node-gyp@^11.0.0
Finally, install sharp
npm install sharp@^0.33.5 --build-from-source
Dockerfile
I assume that you already includes node-gyp, node-addon-api, and sharp on your package.json file.
{
"dependencies": {
"node-addon-api": "^8.1.0",
"node-gyp": "^11.0.0",
"sharp": "^0.33.5"
}
}
Here's the Dockerfile looks like:
# Stage 1
FROM debian:bookworm-slim AS builder
WORKDIR /build
# Install dependencies
RUN apt-get update && \
apt-get install -y curl python3 git build-essential pkg-config && \
rm -rf /var/lib/apt/lists/*
# Install ninja
RUN git clone --branch=v1.12.1 --depth=1 https://github.com/ninja-build/ninja.git && \
cd ninja && \
./configure.py --bootstrap && \
chmod +x ninja && \
mv ninja /usr/local/bin/ninja
# Install meson
RUN git clone --branch=1.6.1 --depth=1 https://github.com/mesonbuild/meson.git && \
cd meson && \
./packaging/create_zipapp.py --outfile meson.pyz --interpreter '/usr/bin/env python3' && \
chmod +x meson.pyz && \
mv meson.pyz /usr/local/bin/meson
# Stage 2
FROM debian:bookworm-slim
# Install runtime dependencies
RUN apt-get update && \
apt-get install -y \
curl python3 build-essential git pkg-config libglib2.0-dev libexpat1-dev libheif-dev\
liblcms2-dev libjpeg-dev libpng-dev libwebp-dev libexif-dev && \
curl -fsSL https://deb.nodesource.com/setup_23.x | bash - && \
apt install -y nodejs
# Copy build tools from builder stage
COPY --from=builder /usr/local/bin/ninja /usr/local/bin/ninja
COPY --from=builder /usr/local/bin/meson /usr/local/bin/meson
# Build libvips
RUN git clone --branch=v8.16.0 --depth=1 https://github.com/libvips/libvips.git && \
cd libvips && \
meson setup build --prefix /usr/local && \
cd build && \
meson compile && \
meson test && \
meson install && \
ldconfig && \
cd .. && \
rm -rf libvips
# Remove unused build tools
RUN apt-get purge -y git && apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/* /usr/local/bin/ninja /usr/local/bin/meson
WORKDIR /app
RUN --mount=type=bind,src=package.json,target=package.json \
--mount=type=bind,src=package-lock.json,target=package-lock.json \
npm ci --build-from-source
COPY . .
# Build the application
RUN npm run build || true
# Remove source code
RUN rm -rf ./src
EXPOSE 3000
ENTRYPOINT [ "npm", "start" ]
Conclusion
Th default sharpjs package doesn't include vips with heic support, it's up to the developer to build vips from source with wider support
Top comments (0)