exif-parser, image-size, image-type, and imageinfo are Node.js utilities designed to inspect image files without fully decoding them. They serve distinct but overlapping roles: image-size extracts dimensions (width/height), image-type identifies the file format (JPEG, PNG, etc.) from buffers or streams, exif-parser reads EXIF metadata (camera settings, GPS, orientation), and imageinfo is a legacy tool that attempts to provide both dimensions and type. These libraries are essential for image upload validation, automatic orientation correction, and optimizing asset delivery in web applications.
When handling image uploads or processing assets in Node.js, you often need to know three things: what format is it, how big is it, and what metadata does it contain? The packages exif-parser, image-size, image-type, and imageinfo address these needs, but they solve different parts of the puzzle. Let's compare how they handle detection, performance, and API design.
image-size is the dedicated tool for reading width and height. It supports many formats (PNG, JPEG, GIF, SVG, etc.) and works with buffers or file paths.
import sizeOf from 'image-size';
const dimensions = sizeOf('images/batch/folder/image.png');
console.log(dimensions.width, dimensions.height);
// Output: 100 100
imageinfo also provides dimensions but uses a callback-based API typical of older Node.js libraries. It returns an object with width, height, and type.
import imageinfo from 'imageinfo';
const buffer = fs.readFileSync('image.png');
const info = imageinfo(buffer);
if (info.valid) {
console.log(info.width, info.height);
}
exif-parser does not provide general image dimensions. It focuses strictly on EXIF data. If the EXIF data contains pixel dimensions, you can read them, but this is not reliable for all images.
// exif-parser: No direct dimension API
// You must parse EXIF tags which may or may not exist
const parser = require('exif-parser').create(buffer);
const tags = parser.parse();
// tags.tags.PixelXDimension might exist, but don't count on it
image-type does not provide dimensions. It only identifies the file format.
// image-type: No dimension support
// Only returns file type info
image-type is the most reliable for checking the actual file signature (magic numbers). It ignores file extensions, which is critical for security.
import imageType from 'image-type';
const buffer = fs.readFileSync('mystery-file');
const type = imageType(buffer);
if (type) {
console.log(type.ext, type.mime);
// Output: 'png', 'image/png'
}
image-size infers the type internally to determine dimensions but does not expose a clean type-detection API as its primary feature. You can access the type property from the result.
import sizeOf from 'image-size';
const dimensions = sizeOf('image.jpg');
console.log(dimensions.type);
// Output: 'jpg'
imageinfo returns the format type alongside dimensions in its result object.
import imageinfo from 'imageinfo';
const info = imageinfo(buffer);
console.log(info.format);
// Output: 'PNG' or 'JPEG'
exif-parser does not detect file types. It assumes the input is a JPEG or TIFF containing EXIF data. Passing a PNG will result in empty or failed parsing.
// exif-parser: Assumes JPEG/TIFF
// No type detection mechanism
const parser = require('exif-parser').create(pngBuffer);
const result = parser.parse();
// Result will likely be empty or invalid
exif-parser is the only package in this list designed specifically for EXIF data. It exposes tags like orientation, GPS, ISO, and shutter speed.
import exif from 'exif-parser';
const parser = exif.create(buffer);
parser.parse((err, result) => {
if (err) throw err;
console.log(result.tags.Make); // Camera brand
console.log(result.tags.Orientation); // For auto-rotate
});
image-size does not read EXIF metadata. It stops after finding the dimension markers.
// image-size: No EXIF support
const dimensions = sizeOf('image.jpg');
// dimensions object has no EXIF tags
image-type does not read EXIF metadata. It only reads the file header signature.
// image-type: No EXIF support
const type = imageType(buffer);
// Returns only ext and mime
imageinfo does not read EXIF metadata. It focuses on basic structural info like size and format.
// imageinfo: No EXIF support
const info = imageinfo(buffer);
// No tags or metadata properties available
image-size supports streams, which is useful for large files where loading the whole buffer into memory is expensive.
import sizeOf from 'image-size';
import fs from 'fs';
const stream = fs.createReadStream('large-image.png');
sizeOf(stream, (err, dimensions) => {
console.log(dimensions.width);
});
image-type also supports streams, allowing type detection without waiting for the full download.
import imageType from 'image-type';
import fs from 'fs';
const stream = fs.createReadStream('upload');
const type = await imageType.stream(stream);
console.log(type?.ext);
exif-parser works primarily with buffers. While you can pipe data into it, the API is designed around having the binary data available to parse tags.
// exif-parser: Buffer focused
const parser = exif.create(buffer);
// Stream support is not a primary feature
imageinfo expects a buffer. It does not have built-in stream handling.
// imageinfo: Buffer only
const info = imageinfo(buffer);
// Must read file fully before calling
image-size is actively maintained and widely used in production ecosystems. It receives updates for new formats and bug fixes.
image-type is also actively maintained and considered the standard for file type detection in the Node.js ecosystem.
exif-parser is stable but sees less frequent updates. It remains the go-to for pure JS EXIF parsing without native dependencies.
imageinfo is deprecated or effectively unmaintained. It has not seen significant updates in years. Using it introduces risk for security and compatibility.
| Feature | image-size | image-type | exif-parser | imageinfo |
|---|---|---|---|---|
| Dimensions | ✅ Yes | ❌ No | ❌ No (indirect) | ✅ Yes |
| File Type | ✅ Yes (inferred) | ✅ Yes (primary) | ❌ No | ✅ Yes |
| EXIF Data | ❌ No | ❌ No | ✅ Yes | ❌ No |
| Stream Support | ✅ Yes | ✅ Yes | ⚠️ Limited | ❌ No |
| Status | 🟢 Active | 🟢 Active | 🟡 Stable | 🔴 Legacy |
These tools are not direct competitors; they are complementary pieces of an image processing pipeline.
image-type should be your first line of defense. Use it to validate uploads before doing anything else. It ensures a file claiming to be a JPEG is actually a JPEG.
image-size is your standard for dimensions. Use it when you need to enforce aspect ratios or calculate storage needs. Its stream support makes it safe for large files.
exif-parser is your specialist for metadata. Use it when you need to auto-rotate images based on orientation tags or extract GPS data for mapping features.
imageinfo should be avoided. Its functionality is split better between image-size and image-type, both of which are more modern and reliable.
Final Thought: For a robust image upload handler, combine image-type (for security), image-size (for validation), and exif-parser (for orientation correction). Do not rely on a single legacy package to do it all.
Choose exif-parser when you need to read EXIF metadata such as GPS coordinates, camera make/model, or orientation data. It is the specialized tool for metadata extraction and does not handle dimension detection or file type identification on its own. Use this alongside a dimension library if you need both size and metadata.
Choose image-size when your primary goal is to retrieve the width and height of an image file quickly. It supports a wide range of formats and works well with both buffers and file paths. It is the standard choice for validation logic where dimensions matter more than file type or metadata.
Choose image-type when you need to detect the actual file format from a buffer or stream, regardless of the file extension. It is ideal for security validation to ensure an uploaded file matches its claimed type. It does not provide dimensions or EXIF data.
Avoid imageinfo for new projects. It is a legacy package that combines dimension and type detection but lacks active maintenance and modern format support. Prefer image-size and image-type for better reliability, wider format coverage, and active community support.
exif-parser is a parser for image metadata in the exif format, the most popular metadata format for jpeg and tiff images. It is written in pure javascript and has no external dependencies. It can also get the size of jpeg images and the size of the jpeg thumbnail embedded in the exif data. It can also extract the embedded thumbnail image.
npm install exif-parser
You can also build a browser bundle to include it with a <script> tag in a HTML document, like this:
git clone git@github.com:bwindels/exif-parser.git
cd exif-parser/
make build-browser-bundle
Built versions of the bundles are also available in the exif-parser-browser-bundles repo.
This will generate a dist/exif-parser-(version).js and dist/exif-parser-(version)-min.js file. These bundles expose the parser on the ExifParser global variable, which you would use like this:
var parser = window.ExifParser.create(arrayBuffer);
To start parsing exif data, create a new parser like below. Note that the buffer you pass does not have to be the buffer for the full jpeg file. The exif section of a jpeg file has a maximum size of 65535 bytes and the section seems to always occur within the first 100 bytes of the file. So it is safe to only fetch the first 65635 bytes of a jpeg file and pass those to the parser.
The buffer you pass to create can be a node buffer or a DOM ArrayBuffer.
var parser = require('exif-parser').create(buffer);
var result = parser.parse();
Before calling parse, you can set a number of flags on the parser, telling it how to behave while parsing.
Add fields in the binary format to result. Since these fields are mostly used for internal fields like Padding, you generally are not interested in these. If enabled, values for these fields will be a Buffer object in node or an ArrayBuffer in DOM environments (browsers).
parser.enableBinaryFields([boolean]), default false;
EXIF tags are organized into different sections, and to tell you the offset to other sections, EXIF uses certain tags. These tags don't tell you anything about the image, but are more for parsers to find out about all tags. Hence, these "pointer" fields are not included in the result tags field by default. Change this flag to include them nonetheless.
parser.enablePointers([boolean]), default false;
Resolve tags to their textual name, making result.tags a dictonary object instead of an array with the tag objects with no textual tag name.
parser.enableTagNames([boolean]), default true;
Read the image size while parsing.
parser.enableImageSize([boolean]), default true;
Read the EXIF tags. Could be useful to disable if you only want to read the image size.
parser.enableReturnTags([boolean]), default true;
EXIF values can be represented in a number of formats (fractions, degrees, arrays, ...) with different precision. Enabling this tries to cast values as much as possible to the appropriate javascript types like number, Date.
parser.enableSimpleValues([boolean]), default true;
the tags that were found while parsing are stored in result.tags unless you set parser.enableReturnTags(false). If parser.enableTagNames is set to true, result.tags will be an object with the key being the tag name and the value being the tag value. If parser.enableTagNames is set to false, result.tags will be an array of objects containing section, type and value properties.
If parser.enableImageSize is set to true, result.getImageSize() will give you the image size as an object with width and height properties.
You can check if there is a thumbnail present in the exif data with result.hasThumbnail(). Exif supports thumbnails is jpeg and tiff format, though most are in jpeg format. You can check if there is a thumbnail present in a give format by passing the mime type: result.hasThumbnail("image/jpeg").
You can also get the image size of the thumbnail as an object with width and height properties: result.getThumbnailSize().
To get the node buffer or arraybuffer containing just the thumbnail, call result.getThumbnailBuffer()
Install nodeunit globally from npm if you haven't done so already.
You can run the tests with nodeunit test/test-*.js.
I welcome external contributions through pull requests. If you do so, please don't use regular expressions. I don't like them, and don't want to maintain a project where they are used. Also, when fixing a bug please provide a regression unit test if it makes sense.