Skip to content

Commit

Permalink
Cache processed images by their source path
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Sep 3, 2019
1 parent 05d83b6 commit 75da86a
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 21 deletions.
86 changes: 74 additions & 12 deletions hugolib/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
package hugolib

import (
"image/jpeg"
"io"
"os"
"path/filepath"
Expand Down Expand Up @@ -100,16 +99,6 @@ title: "My bundle"
b := newBuilder()
b.Build(BuildCfg{})

assertImage := func(width, height int, filename string) {
filename = filepath.Join(workDir, "public", filename)
f, err := b.Fs.Destination.Open(filename)
c.Assert(err, qt.IsNil)
defer f.Close()
cfg, err := jpeg.DecodeConfig(f)
c.Assert(cfg.Width, qt.Equals, width)
c.Assert(cfg.Height, qt.Equals, height)
}

imgExpect := `
Resized1: images/sunset.jpg|123|234|image/jpg|/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_123x234_resize_q75_box.jpg|
Resized2: images/sunset.jpg|12|23|image/jpg|/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_ada4bb1a57f77a63306e3bd67286248e.jpg|
Expand All @@ -121,7 +110,7 @@ Resized6: images/sunset.jpg|350|219|image/jpg|/images/sunset_hu59e56ffff1bc1d8d1
`

b.AssertFileContent(filepath.Join(workDir, "public/index.html"), imgExpect)
assertImage(350, 219, "images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_350x0_resize_q75_box.a86fe88d894e5db613f6aa8a80538fefc25b20fa24ba0d782c057adcef616f56.jpg")
b.AssertImage(350, 219, "public/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_350x0_resize_q75_box.a86fe88d894e5db613f6aa8a80538fefc25b20fa24ba0d782c057adcef616f56.jpg")

// Build it again to make sure we read images from file cache.
b = newBuilder()
Expand All @@ -130,3 +119,76 @@ Resized6: images/sunset.jpg|350|219|image/jpg|/images/sunset_hu59e56ffff1bc1d8d1
b.AssertFileContent(filepath.Join(workDir, "public/index.html"), imgExpect)

}

func TestImageResizeMultilingual(t *testing.T) {

b := newTestSitesBuilder(t).WithConfigFile("toml", `
baseURL="https://example.org"
defaultContentLanguage = "en"
[languages]
[languages.en]
title = "Title in English"
languageName = "English"
weight = 1
[languages.nn]
languageName = "Nynorsk"
weight = 2
title = "Tittel på nynorsk"
[languages.nb]
languageName = "Bokmål"
weight = 3
title = "Tittel på bokmål"
[languages.fr]
languageName = "French"
weight = 4
title = "French Title"
`)

pageContent := `---
title: "Page"
---
`

b.WithContent("bundle/index.md", pageContent)
b.WithContent("bundle/index.nn.md", pageContent)
b.WithContent("bundle/index.fr.md", pageContent)
b.WithSunset("content/bundle/sunset.jpg")
b.WithSunset("assets/images/sunset.jpg")
b.WithTemplates("index.html", `
{{ with (.Site.GetPage "bundle" ) }}
{{ $sunset := .Resources.GetMatch "sunset*" }}
{{ if $sunset }}
{{ $resized := $sunset.Resize "200x200" }}
SUNSET FOR: {{ $.Site.Language.Lang }}: {{ $resized.RelPermalink }}/{{ $resized.Width }}/Lat: {{ $resized.Exif.Lat }}
{{ end }}
{{ else }}
No bundle for {{ $.Site.Language.Lang }}
{{ end }}
{{ $sunset2 := resources.Get "images/sunset.jpg" }}
{{ $resized2 := $sunset2.Resize "123x234" }}
SUNSET2: {{ $resized2.RelPermalink }}/{{ $resized2.Width }}/Lat: {{ $resized2.Exif.Lat }}
`)

b.Build(BuildCfg{})

b.AssertFileContent("public/index.html", "SUNSET FOR: en: /bundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_resize_q75_box.jpg/200/Lat: 36.59744166666667")
b.AssertFileContent("public/fr/index.html", "SUNSET FOR: fr: /fr/bundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_resize_q75_box.jpg/200/Lat: 36.59744166666667")
b.AssertFileContent("public/index.html", " SUNSET2: /images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_123x234_resize_q75_box.jpg/123/Lat: 36.59744166666667")
b.AssertFileContent("public/nn/index.html", " SUNSET2: /images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_123x234_resize_q75_box.jpg/123/Lat: 36.59744166666667")

b.AssertImage(200, 200, "public/fr/bundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_resize_q75_box.jpg")
b.AssertImage(200, 200, "public/bundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_resize_q75_box.jpg")

// Check the file cache
b.AssertImage(200, 200, "resources/_gen/images/bundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_resize_q75_box.jpg")
b.AssertFileContent("resources/_gen/images/bundle/sunset_17701188623491591036.json",
"DateTimeDigitized|time.Time", "PENTAX")
b.AssertImage(123, 234, "resources/_gen/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_123x234_resize_q75_box.jpg")
b.AssertFileContent("resources/_gen/images/sunset_17701188623491591036.json",
"DateTimeDigitized|time.Time", "PENTAX")

}
13 changes: 12 additions & 1 deletion hugolib/testhelpers_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hugolib

import (
"image/jpeg"
"io"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -334,7 +335,7 @@ func (s *sitesBuilder) WithSunset(in string) {
src, err := os.Open(filepath.FromSlash("testdata/sunset.jpg"))
s.Assert(err, qt.IsNil)

out, err := s.Fs.Source.Create(filepath.FromSlash(in))
out, err := s.Fs.Source.Create(filepath.FromSlash(filepath.Join(s.workingDir, in)))
s.Assert(err, qt.IsNil)

_, err = io.Copy(out, src)
Expand Down Expand Up @@ -669,6 +670,16 @@ func (s *sitesBuilder) AssertFileContent(filename string, matches ...string) {
}
}

func (s *sitesBuilder) AssertImage(width, height int, filename string) {
filename = filepath.Join(s.workingDir, filename)
f, err := s.Fs.Destination.Open(filename)
s.Assert(err, qt.IsNil)
defer f.Close()
cfg, err := jpeg.DecodeConfig(f)
s.Assert(cfg.Width, qt.Equals, width)
s.Assert(cfg.Height, qt.Equals, height)
}

func (s *sitesBuilder) FileContent(filename string) string {
s.T.Helper()
filename = filepath.FromSlash(filename)
Expand Down
6 changes: 5 additions & 1 deletion resources/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"sync"

Expand Down Expand Up @@ -319,10 +320,13 @@ func (i *imageResource) getImageMetaCacheTargetPath() string {

cfg := i.getSpec().imaging.Cfg
df := i.getResourcePaths().relTargetDirFile
if fi := i.getFileInfo(); fi != nil {
df.dir = filepath.Dir(fi.Meta().Path())
}
p1, _ := helpers.FileAndExt(df.file)
h, _ := i.hash()
idStr := internal.HashString(h, i.size(), imageMetaVersionNumber, cfg)
return path.Join(df.dir, fmt.Sprintf("%s%s.json", p1, idStr))
return path.Join(df.dir, fmt.Sprintf("%s_%s.json", p1, idStr))
}

func (i *imageResource) relTargetPathFromConfig(conf images.ImageConfig) dirFile {
Expand Down
17 changes: 12 additions & 5 deletions resources/image_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,18 @@ func (c *imageCache) getOrCreate(
parent *imageResource, conf images.ImageConfig,
createImage func() (*imageResource, image.Image, error)) (*resourceAdapter, error) {
relTarget := parent.relTargetPathFromConfig(conf)
key := parent.relTargetPathForRel(relTarget.path(), false, false, false)
memKey := parent.relTargetPathForRel(relTarget.path(), false, false, false)

// For the file cache we want to generate and store it once if possible.
fileKeyPath := relTarget
if fi := parent.root.getFileInfo(); fi != nil {
fileKeyPath.dir = filepath.ToSlash(filepath.Dir(fi.Meta().Path()))
}
fileKey := fileKeyPath.path()

// First check the in-memory store, then the disk.
c.mu.RLock()
cachedImage, found := c.store[key]
cachedImage, found := c.store[memKey]
c.mu.RUnlock()

if found {
Expand Down Expand Up @@ -133,7 +140,7 @@ func (c *imageCache) getOrCreate(
// but the count of processed image variations for this site.
c.pathSpec.ProcessingStats.Incr(&c.pathSpec.ProcessingStats.ProcessedImages)

_, err := c.fileCache.ReadOrCreate(key, read, create)
_, err := c.fileCache.ReadOrCreate(fileKey, read, create)
if err != nil {
return nil, err
}
Expand All @@ -142,13 +149,13 @@ func (c *imageCache) getOrCreate(
img.setSourceFs(c.fileCache.Fs)

c.mu.Lock()
if cachedImage, found = c.store[key]; found {
if cachedImage, found = c.store[memKey]; found {
c.mu.Unlock()
return cachedImage, nil
}

imgAdapter := newResourceAdapter(parent.getSpec(), true, img)
c.store[key] = imgAdapter
c.store[memKey] = imgAdapter
c.mu.Unlock()

return imgAdapter, nil
Expand Down
9 changes: 8 additions & 1 deletion resources/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"path/filepath"
"sync"

"github.com/gohugoio/hugo/hugofs"

"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/source"

Expand Down Expand Up @@ -172,6 +174,7 @@ type fileInfo interface {
getSourceFilename() string
setSourceFilename(string)
setSourceFs(afero.Fs)
getFileInfo() hugofs.FileMetaInfo
hash() (string, error)
size() int
}
Expand Down Expand Up @@ -537,7 +540,7 @@ type resourceFileInfo struct {
// the path to the file on the real filesystem.
sourceFilename string

fi os.FileInfo
fi hugofs.FileMetaInfo

// A hash of the source content. Is only calculated in caching situations.
h *resourceHash
Expand All @@ -555,6 +558,10 @@ func (fi *resourceFileInfo) ReadSeekCloser() (hugio.ReadSeekCloser, error) {
return f, nil
}

func (fi *resourceFileInfo) getFileInfo() hugofs.FileMetaInfo {
return fi.fi
}

func (fi *resourceFileInfo) getSourceFilename() string {
return fi.sourceFilename
}
Expand Down
9 changes: 8 additions & 1 deletion resources/resource_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"path/filepath"
"strings"

"github.com/gohugoio/hugo/hugofs"

"github.com/gohugoio/hugo/helpers"

"github.com/gohugoio/hugo/cache/filecache"
Expand Down Expand Up @@ -194,8 +196,13 @@ func (r *Spec) newGenericResourceWithBase(
relTargetDirFile: dirFile{dir: fpath, file: fname},
}

var fim hugofs.FileMetaInfo
if osFileInfo != nil {
fim = osFileInfo.(hugofs.FileMetaInfo)
}

gfi := &resourceFileInfo{
fi: osFileInfo,
fi: fim,
openReadSeekerCloser: openReadSeekerCloser,
sourceFs: sourceFs,
sourceFilename: sourceFilename,
Expand Down

0 comments on commit 75da86a

Please sign in to comment.