Skip to content

Read from files and write to disk

Copy page

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.

Gate of the Orient night photograph used as the JPEG input file
Input file: JPEG1938 × 1292, 610,191 bytes
Night scene loaded from disk and written back as WebP
Output file: WebP1938 × 1292, 399,106 bytes (govips reference output)

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)
}
}
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

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), or warning (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=-1 loads all frames), and vector rasterization Density (DPI) at load time.

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)
}