janko / image_processing

High-level image processing wrapper for libvips and ImageMagick/GraphicsMagick
MIT License
840 stars 74 forks source link

VipsForeignLoad: "/tmp/ActiveStorage-28657-20220112-4-8e2faa.jpg" is not a known file format #97

Closed dorianmariecom closed 2 years ago

dorianmariecom commented 2 years ago

Bug report

Describe the bug

Somehow when trying to process an image I got this error:

VipsForeignLoad: "/tmp/ActiveStorage-28657-20220112-4-8e2faa.jpg" is not a known file format

To Reproduce

Steps to reproduce the behavior:

  1. user uploads an image
  2. I transform it using rails's active storage attachment.file.variant(**options).processed
  3. See error

Expected behavior

Either says the file is not found somehow (maybe because of a server/dyno restart?) (I'm on Heroku), or maybe a missing package? not sure

Or actually process the file and return a new processed file

Actual behavior

Errors out

Environment

Additional context

I'm on a rails app, users can upload images, I process them (size, remove filenames, remove metadata, optimize, etc.)

dorianmariecom commented 2 years ago

From the original issue at https://github.com/libvips/libvips/issues/2620

tsrivishnu commented 2 years ago

We are facing the same issue with all jpg files. Below is the code that triggers the error with Rails ActiveStorage

User.cover_picture.variant(resize_to_fill: [640, 360]).processed

# => Vips::Error: VipsForeignLoad: "/tmp/ActiveStorage-3732-20220120-1268611-1q3jp07" is not a known file format
#    VipsForeignLoad: "/tmp/ActiveStorage-3732-20220120-1268611-1q3jp07" is not a known file format

Happens on vips-8.4.5 and also vips-8.12.1 with image_processor version 1.12.1.

Here is a sample image that we are uplaoding: Pale blue dot - wallpaper

janko commented 2 years ago

I don't know what could be the issue. I would need a sample Rails app along with an image that reproduces the problem. Or a self-contained script using only ImageProcessing, without Active Storage.

tsrivishnu commented 2 years ago

Sure. I will prepare one and send in a day or two.

On 20. Jan 2022, at 19:05, Janko Marohnić @.***> wrote:

 I don't know what could be the issue. I would need a sample Rails app along with an image that reproduces the problem. Or a self-contained script using only ImageProcessing, without Active Storage.

— Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android. You are receiving this because you commented.

tsrivishnu commented 2 years ago

@janko Here is a sample app: https://github.com/tsrivishnu/tmp-rails-libvips-jpg-failures that you can run in docker. The README has the instructions to run it in Docker. Make sure you adapt the Dockerfile as in the first point under Setup. Let me know if you are able to reproduce it.

tsrivishnu commented 2 years ago

Looks like the issue occurs only on Ubuntu bionic. I tried the same setup on Debian buster and it doesn't happen there.

janko commented 2 years ago

Is there some way to verify that libvips has been compiled with JPEG support? What does running Vips.get_suffixes return for you inside a Rails console in the Docker container?

tsrivishnu commented 2 years ago

I see the following in the Docker container where the variant creation fails

irb(main):007:0> Vips.get_suffixes
=> [".csv", ".mat", ".raw", ".v", ".vips", ".pbm", ".pgm", ".ppm", ".pfm", ".hdr", ".png"]

On the contrary, on a different container where it the variant creation succeeds:

irb(main):005:0> Vips.get_suffixes
=> [".csv", ".mat", ".raw", ".v", ".vips", ".ppm", ".pgm", ".pbm", ".pfm", ".hdr", ".dz", ".png", ".jpg", ".jpeg", ".jpe", ".webp", ".tif", ".tiff", ".fits", ".fit", ".fts", ".heic", ".heif", ".avif", ".gif", ".bmp"]
janko commented 2 years ago

OK, this means the issue is neither in Active Storage nor in ImageProcessing, but in libvips provided by Ubuntu Bionic not being compiled with JPEG support.

tsrivishnu commented 2 years ago

For those who land on this issue looking for a solution:

Easiest option is to install libvips-dev so that it installs all the require dependancies. This will install all the required libraries to support all the major file formats.

   $ sudo apt-get install libvips-dev

If you are building libvips from source you need to install the optional dependancies yourself before you install libvips. You will see in libvips install instructions that it has many optional dependancies: https://www.libvips.org/install.html.

Pick the libraries required for the file formats you want to support or almost all of them with the following before you try to build from source. The following will install support for all major formats.

$ sudo apt-get install libjpeg-turbo8-dev \
                     libexif-dev \
                     libgif-dev \
                     librsvg2-dev \
                     libpoppler-glib-dev \
                     libtiff-dev \
                     libfftw3-dev \
                     libpng-dev \
                     liborc-0.4-dev \
                     libcfitsio-dev \
                     libwebp-dev \
                     libnifti-dev \
                     libheif-dev \
                     libopenslide-dev \
                     libgsf-1-dev \
                     libopenexr-dev \
                     libmatio-dev \
                     libmagickcore-dev \
                     libmagickwand-dev \
                     libimagequant-dev
jcupitt commented 2 years ago

Is there some way to verify that libvips has been compiled with JPEG support? What does running Vips.get_suffixes return for you inside a Rails console in the Docker container?

You can use --vips-config, eg. I see:

$ vips --vips-config
enable debug: no
enable deprecated library components: yes
enable modules: no
use fftw3 for FFT: yes
accelerate loops with orc: yes
ICC profile support with lcms: yes (lcms2)
zlib: yes
text rendering with pangocairo: yes
font file support with fontconfig: yes
RAD load/save: yes
Analyze7 load/save: yes
PPM load/save: yes
GIF load:  yes
GIF save with cgif: yes
EXIF metadata support with libexif: yes
JPEG load/save with libjpeg: yes (pkg-config)
JXL load/save with libjxl: no (dynamic module: no)
JPEG2000 load/save with libopenjp2: no
PNG load with libspng: no
PNG load/save with libpng: yes (pkg-config libpng >= 1.2.9)
quantisation to 8 bit: yes
TIFF load/save with libtiff: yes (pkg-config libtiff-4)
image pyramid save: yes
HEIC/AVIF load/save with libheif: yes (dynamic module: no)
WebP load/save with libwebp: yes
PDF load with PDFium:  no
PDF load with poppler-glib: yes (dynamic module: no)
SVG load with librsvg-2.0: yes
EXR load with OpenEXR: yes
OpenSlide load: yes (dynamic module: no)
Matlab load with matio: yes
NIfTI load/save with niftiio: yes
FITS load/save with cfitsio: yes
Magick package: MagickCore (dynamic module: no)
Magick API version: magick6
load with libMagickCore: yes
save with libMagickCore: yes
jcupitt commented 2 years ago

Many of the load libraries that libvips can use are not very well tested and probably have vulnerabilities. The standard Debian and Ubuntu builds, for example, enable almost everything, and this makes them unsuitable for some applications.

If you are processing untrusted inputs (eg. a public web site) I would build your own libvips and only enable core formats. We fuzz with this set of loaders:

https://github.com/google/oss-fuzz/blob/master/projects/libvips/build.sh

Update alternatively, libvips 8.13 has a new feature which lets you disable untested loaders. Just set the env var VIPS_BLOCK_UNTRUSTED and you can safely use any libvips binary.

See:

https://www.libvips.org/2022/05/28/What's-new-in-8.13.html#blocking-of-unfuzzed-loaders

tsrivishnu commented 2 years ago

Thank you @jcupitt

gathuku commented 1 year ago

Why would an ActiveStorage::Blob.download return "" Does this means the file was not uploaded?

This is how the blob record looks like

#<ActiveStorage::Blob:0x00007fcffec70170
 id: 9175,
 key: "cyrlrcoiotkfyzhn923dz1elpm",
 filename: "Image.jpeg",
 content_type: "image/jpeg",
 metadata: {"identified"=>true, "analyzed"=>true},
 service_name: "amazon",
 byte_size: 0,
 checksum: "1B2M2Y8AsgTpgAmhCfg==",
 created_at: Tue, 20 Sep 2022 00:42:27.524396000 UTC +00:00>

When you try to read the same file with vips you get this issue.

 photos.first.blob.open do |file|
   Vips::Image.new_from_file file.path
 end

Error:

/Users/gathuku/.asdf/installs/ruby/3.0.3/lib/ruby/gems/3.0.0/gems/ruby-vips-2.1.4/lib/vips/image.rb:281:in `new_from_file': VipsForeignLoad: "/var/folders/ch/8_2mrh555z9_19b8lzgkf44c0000gp/T/ActiveStorage-9175-20220926-91104-3ubaml.jpeg" is not a known file format (Vips::Error)
Mth0158 commented 7 months ago

Hi, I have been stumbling on this problem lately, as I understand it, the VipsForeignLoad is raised when passing a file with a 0 byte_size (cf @gathuku example).

Vips::Error Exception: VipsForeignLoad:
"path/to/file/image_file_0ko.png" is not a known file format

This edge case works fine in with MiniMagick. If you use the active_storage_validation gem, you can handle this use case with the processable_image validator. I'll fix this issue in the active_storage_validation gem in the coming days.

jcupitt commented 7 months ago

You can get zero-length files if you write to a file you're also reading. For example:

$ ls -l k2.png
-rw-r--r-- 1 john john 6126529 Dec  8 16:48 k2.png
$ vips copy k2.png k2.png

(vips:830444): VIPS-WARNING **: 16:48:13.378: not enough data
vips2png: unable to write to target k2.png
error buffer: vips2png: unable to write to target k2.png
$ ls -l k2.png
-rw-r--r-- 1 john john 0 Dec  8 16:48 k2.png

Could that be happening here somehow?

(this happens because libvips is lazy --- it won't process any pixels until they are really needed (to write the first byte of the output file in this case) and by the time you get to that point, the input file has already been deleted)

Mth0158 commented 7 months ago

Hi @jcupitt, In my context, it's all about reading a 0 byte file that has been inputed in a form. At some point in the code we run Vips::Image.new_from_file(path) with path being the uploaded file path, this code raises the mentioned error (Vips::Error Exception: VipsForeignLoad: "path/to/file/image_file_0ko.png" is not a known file format). So we do not write, we only read. Is this the normal behaviour?

jcupitt commented 7 months ago

Yes, that's expected behaviour. I think we used to special-case zero length files and have a different error message, but we removed it for simplicity.

Would you prefer a special message for this?

Mth0158 commented 7 months ago

@jcupitt No it's fine for me. I will just catch this error to invalidate the fact that the image is processable and that's it. Thanks for your explanations.