Read from files and write to disk
File I/O sits at both ends of most image pipelines. Although all four interfaces delegate to the same libvips format loaders (jpegload, pngload, webpload) and savers (jpegsave, webpsave), they differ in how loader options are passed and whether a direct file-writing helper exists.


Explicit loader parameters and disk export
Section titled “Explicit loader parameters and disk export”In production services, pass loader options explicitly — such as the access hint (AccessSequential) and corruption policy (FailOn) — rather than relying on zero-argument defaults.
Note a key API boundary: govips has no SaveToFile method on ImageRef. To write a file in govips, either export encoded bytes with ExportWebp / ExportJpeg / ExportPng and pass the slice to os.WriteFile, or open an *os.File and pass it to the SaveToWriter* methods introduced in govips v2.19.0.
package main
import ( "fmt" "os"
"github.com/davidbyttow/govips/v2/vips")
func main() { if err := vips.Startup(nil); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer vips.Shutdown()
importParams := vips.NewImportParams() importParams.Access.Set(vips.AccessSequential) importParams.FailOn.Set(vips.FailOnError)
image, err := vips.LoadImageFromFile("input.jpg", importParams) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer image.Close()
exportParams := vips.NewWebpExportParams() exportParams.Quality = 82 exportParams.StripMetadata = true
data, _, err := image.ExportWebp(exportParams) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if err := os.WriteFile("output.webp", data, 0o644); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) }}import sharp from 'sharp';
const info = await sharp('input.jpg', { sequentialRead: true, failOn: 'error',}) .webp({ quality: 82 }) .toFile('output.webp');
console.log(`${info.format} ${info.width}x${info.height} (${info.size} bytes)`);import pyvips
image = pyvips.Image.new_from_file( "input.jpg", access="sequential", fail_on="error",)image.write_to_file("output.webp", Q=82, strip=True)#!/usr/bin/env bashset -euo pipefail
vips copy 'input.jpg[access=sequential,fail-on=error]' 'output.webp[Q=82,strip=true]'File I/O comparison across interfaces
Section titled “File I/O comparison across interfaces”| Aspect | govips v2.20.0 | sharp 0.35.5 | pyvips 3.2.0 | vips CLI 8.17.3 |
|---|---|---|---|---|
| Default file loader | NewImageFromFile(path) |
sharp(path) |
Image.new_from_file(path) |
File path argument |
| Parameterized file loader | LoadImageFromFile(path, params) |
sharp(path, options) |
Image.new_from_file(path, **kwargs) |
'path[key=val,...]' |
| Direct file saver | None (Export* + os.WriteFile or SaveToWriter*) |
.toFile(outPath) (returns OutputInfo) |
.write_to_file(outPath, **kwargs) |
'outPath[key=val,...]' |
| Same-path overwrite guard | Caller responsibility | Rejects with Cannot use same file for input and output |
Not blocked; truncates source during sequential read | Not blocked; truncates source during sequential read |
Common loader options
Section titled “Common loader options”Supplying loader hints lets the decoder skip unnecessary work while opening the file:
- Access mode:
vips.AccessSequential/sequentialRead: true/access="sequential"instructs libvips to stream scanlines from top to bottom. Use it for single-pass resize, crop, and transcode pipelines, and avoid it when downstream operations read rows out of order. - Fail-on policy: controls how strictly the loader reacts to damaged files:
none(decode as much as possible),truncated(fail on truncated input),error(fail on decoder errors), orwarning(abort on any warning, suited for strict untrusted upload validation). - Page selection and DPI: for animated GIF/WebP or multi-page PDF/TIFF inputs, set
Page,NumPages(n=-1loads all frames), and vector rasterizationDensity(DPI) at load time.
Atomic replacement with a temporary file
Section titled “Atomic replacement with a temporary file”When updating an existing file path on disk, write to a sibling temporary file first and rename it once encoding succeeds:
func writeAtomically(targetPath string, data []byte) error { tmpPath := targetPath + ".tmp" if err := os.WriteFile(tmpPath, data, 0o644); err != nil { return err } return os.Rename(tmpPath, targetPath)}import { rename } from 'node:fs/promises';import sharp from 'sharp';
const tmpPath = 'output.webp.tmp';await sharp('input.jpg').webp({ quality: 82 }).toFile(tmpPath);await rename(tmpPath, 'output.webp');import osimport pyvips
tmp_path = "output.webp.tmp"image = pyvips.Image.new_from_file("input.jpg", access="sequential")image.write_to_file(tmp_path, Q=82)os.replace(tmp_path, "output.webp")vips copy 'input.jpg[access=sequential]' 'output.webp.tmp[Q=82]'mv -f output.webp.tmp output.webpWhere to go next
Section titled “Where to go next”- When images arrive as in-memory byte slices, HTTP bodies, or object-storage streams, continue to In-memory buffers, raw pixels, and host image interop and Reader/Writer streaming I/O.
- To inspect format, dimensions, and metadata without decoding every pixel, see Format probing, metadata inspection, and pixel statistics.